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(Roo.isAndroid);
7198             
7199             if(Roo.isAndroid){
7200                 alert('Is Android');
7201                 return Roo.get(document.documentElement);
7202             }
7203             
7204             if(!Roo.isAndroid){
7205                 alert('not android');
7206             }
7207             
7208             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7209         },
7210
7211         /**
7212          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7213          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7214          * @param {String} selector The simple selector to test
7215          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7216                 search as a number or element (defaults to 10 || document.body)
7217          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7218          */
7219         up : function(simpleSelector, maxDepth){
7220             return this.findParentNode(simpleSelector, maxDepth, true);
7221         },
7222
7223
7224
7225         /**
7226          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7227          * @param {String} selector The simple selector to test
7228          * @return {Boolean} True if this element matches the selector, else false
7229          */
7230         is : function(simpleSelector){
7231             return Roo.DomQuery.is(this.dom, simpleSelector);
7232         },
7233
7234         /**
7235          * Perform animation on this element.
7236          * @param {Object} args The YUI animation control args
7237          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7238          * @param {Function} onComplete (optional) Function to call when animation completes
7239          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7240          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7241          * @return {Roo.Element} this
7242          */
7243         animate : function(args, duration, onComplete, easing, animType){
7244             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7245             return this;
7246         },
7247
7248         /*
7249          * @private Internal animation call
7250          */
7251         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7252             animType = animType || 'run';
7253             opt = opt || {};
7254             var anim = Roo.lib.Anim[animType](
7255                 this.dom, args,
7256                 (opt.duration || defaultDur) || .35,
7257                 (opt.easing || defaultEase) || 'easeOut',
7258                 function(){
7259                     Roo.callback(cb, this);
7260                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7261                 },
7262                 this
7263             );
7264             opt.anim = anim;
7265             return anim;
7266         },
7267
7268         // private legacy anim prep
7269         preanim : function(a, i){
7270             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7271         },
7272
7273         /**
7274          * Removes worthless text nodes
7275          * @param {Boolean} forceReclean (optional) By default the element
7276          * keeps track if it has been cleaned already so
7277          * you can call this over and over. However, if you update the element and
7278          * need to force a reclean, you can pass true.
7279          */
7280         clean : function(forceReclean){
7281             if(this.isCleaned && forceReclean !== true){
7282                 return this;
7283             }
7284             var ns = /\S/;
7285             var d = this.dom, n = d.firstChild, ni = -1;
7286             while(n){
7287                 var nx = n.nextSibling;
7288                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7289                     d.removeChild(n);
7290                 }else{
7291                     n.nodeIndex = ++ni;
7292                 }
7293                 n = nx;
7294             }
7295             this.isCleaned = true;
7296             return this;
7297         },
7298
7299         // private
7300         calcOffsetsTo : function(el){
7301             el = Roo.get(el);
7302             var d = el.dom;
7303             var restorePos = false;
7304             if(el.getStyle('position') == 'static'){
7305                 el.position('relative');
7306                 restorePos = true;
7307             }
7308             var x = 0, y =0;
7309             var op = this.dom;
7310             while(op && op != d && op.tagName != 'HTML'){
7311                 x+= op.offsetLeft;
7312                 y+= op.offsetTop;
7313                 op = op.offsetParent;
7314             }
7315             if(restorePos){
7316                 el.position('static');
7317             }
7318             return [x, y];
7319         },
7320
7321         /**
7322          * Scrolls this element into view within the passed container.
7323          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7324          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7325          * @return {Roo.Element} this
7326          */
7327         scrollIntoView : function(container, hscroll){
7328             var c = Roo.getDom(container) || document.body;
7329             var el = this.dom;
7330
7331             var o = this.calcOffsetsTo(c),
7332                 l = o[0],
7333                 t = o[1],
7334                 b = t+el.offsetHeight,
7335                 r = l+el.offsetWidth;
7336
7337             var ch = c.clientHeight;
7338             var ct = parseInt(c.scrollTop, 10);
7339             var cl = parseInt(c.scrollLeft, 10);
7340             var cb = ct + ch;
7341             var cr = cl + c.clientWidth;
7342
7343             if(t < ct){
7344                 c.scrollTop = t;
7345             }else if(b > cb){
7346                 c.scrollTop = b-ch;
7347             }
7348
7349             if(hscroll !== false){
7350                 if(l < cl){
7351                     c.scrollLeft = l;
7352                 }else if(r > cr){
7353                     c.scrollLeft = r-c.clientWidth;
7354                 }
7355             }
7356             return this;
7357         },
7358
7359         // private
7360         scrollChildIntoView : function(child, hscroll){
7361             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7362         },
7363
7364         /**
7365          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7366          * the new height may not be available immediately.
7367          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7368          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7369          * @param {Function} onComplete (optional) Function to call when animation completes
7370          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7371          * @return {Roo.Element} this
7372          */
7373         autoHeight : function(animate, duration, onComplete, easing){
7374             var oldHeight = this.getHeight();
7375             this.clip();
7376             this.setHeight(1); // force clipping
7377             setTimeout(function(){
7378                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7379                 if(!animate){
7380                     this.setHeight(height);
7381                     this.unclip();
7382                     if(typeof onComplete == "function"){
7383                         onComplete();
7384                     }
7385                 }else{
7386                     this.setHeight(oldHeight); // restore original height
7387                     this.setHeight(height, animate, duration, function(){
7388                         this.unclip();
7389                         if(typeof onComplete == "function") { onComplete(); }
7390                     }.createDelegate(this), easing);
7391                 }
7392             }.createDelegate(this), 0);
7393             return this;
7394         },
7395
7396         /**
7397          * Returns true if this element is an ancestor of the passed element
7398          * @param {HTMLElement/String} el The element to check
7399          * @return {Boolean} True if this element is an ancestor of el, else false
7400          */
7401         contains : function(el){
7402             if(!el){return false;}
7403             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7404         },
7405
7406         /**
7407          * Checks whether the element is currently visible using both visibility and display properties.
7408          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7409          * @return {Boolean} True if the element is currently visible, else false
7410          */
7411         isVisible : function(deep) {
7412             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7413             if(deep !== true || !vis){
7414                 return vis;
7415             }
7416             var p = this.dom.parentNode;
7417             while(p && p.tagName.toLowerCase() != "body"){
7418                 if(!Roo.fly(p, '_isVisible').isVisible()){
7419                     return false;
7420                 }
7421                 p = p.parentNode;
7422             }
7423             return true;
7424         },
7425
7426         /**
7427          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7428          * @param {String} selector The CSS selector
7429          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7430          * @return {CompositeElement/CompositeElementLite} The composite element
7431          */
7432         select : function(selector, unique){
7433             return El.select(selector, unique, this.dom);
7434         },
7435
7436         /**
7437          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7438          * @param {String} selector The CSS selector
7439          * @return {Array} An array of the matched nodes
7440          */
7441         query : function(selector, unique){
7442             return Roo.DomQuery.select(selector, this.dom);
7443         },
7444
7445         /**
7446          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7447          * @param {String} selector The CSS selector
7448          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7449          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7450          */
7451         child : function(selector, returnDom){
7452             var n = Roo.DomQuery.selectNode(selector, this.dom);
7453             return returnDom ? n : Roo.get(n);
7454         },
7455
7456         /**
7457          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7458          * @param {String} selector The CSS selector
7459          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7460          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7461          */
7462         down : function(selector, returnDom){
7463             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7464             return returnDom ? n : Roo.get(n);
7465         },
7466
7467         /**
7468          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7469          * @param {String} group The group the DD object is member of
7470          * @param {Object} config The DD config object
7471          * @param {Object} overrides An object containing methods to override/implement on the DD object
7472          * @return {Roo.dd.DD} The DD object
7473          */
7474         initDD : function(group, config, overrides){
7475             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7476             return Roo.apply(dd, overrides);
7477         },
7478
7479         /**
7480          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7481          * @param {String} group The group the DDProxy object is member of
7482          * @param {Object} config The DDProxy config object
7483          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7484          * @return {Roo.dd.DDProxy} The DDProxy object
7485          */
7486         initDDProxy : function(group, config, overrides){
7487             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7488             return Roo.apply(dd, overrides);
7489         },
7490
7491         /**
7492          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7493          * @param {String} group The group the DDTarget object is member of
7494          * @param {Object} config The DDTarget config object
7495          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7496          * @return {Roo.dd.DDTarget} The DDTarget object
7497          */
7498         initDDTarget : function(group, config, overrides){
7499             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7500             return Roo.apply(dd, overrides);
7501         },
7502
7503         /**
7504          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7505          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7506          * @param {Boolean} visible Whether the element is visible
7507          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7508          * @return {Roo.Element} this
7509          */
7510          setVisible : function(visible, animate){
7511             if(!animate || !A){
7512                 if(this.visibilityMode == El.DISPLAY){
7513                     this.setDisplayed(visible);
7514                 }else{
7515                     this.fixDisplay();
7516                     this.dom.style.visibility = visible ? "visible" : "hidden";
7517                 }
7518             }else{
7519                 // closure for composites
7520                 var dom = this.dom;
7521                 var visMode = this.visibilityMode;
7522                 if(visible){
7523                     this.setOpacity(.01);
7524                     this.setVisible(true);
7525                 }
7526                 this.anim({opacity: { to: (visible?1:0) }},
7527                       this.preanim(arguments, 1),
7528                       null, .35, 'easeIn', function(){
7529                          if(!visible){
7530                              if(visMode == El.DISPLAY){
7531                                  dom.style.display = "none";
7532                              }else{
7533                                  dom.style.visibility = "hidden";
7534                              }
7535                              Roo.get(dom).setOpacity(1);
7536                          }
7537                      });
7538             }
7539             return this;
7540         },
7541
7542         /**
7543          * Returns true if display is not "none"
7544          * @return {Boolean}
7545          */
7546         isDisplayed : function() {
7547             return this.getStyle("display") != "none";
7548         },
7549
7550         /**
7551          * Toggles the element's visibility or display, depending on visibility mode.
7552          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7553          * @return {Roo.Element} this
7554          */
7555         toggle : function(animate){
7556             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7557             return this;
7558         },
7559
7560         /**
7561          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7562          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7563          * @return {Roo.Element} this
7564          */
7565         setDisplayed : function(value) {
7566             if(typeof value == "boolean"){
7567                value = value ? this.originalDisplay : "none";
7568             }
7569             this.setStyle("display", value);
7570             return this;
7571         },
7572
7573         /**
7574          * Tries to focus the element. Any exceptions are caught and ignored.
7575          * @return {Roo.Element} this
7576          */
7577         focus : function() {
7578             try{
7579                 this.dom.focus();
7580             }catch(e){}
7581             return this;
7582         },
7583
7584         /**
7585          * Tries to blur the element. Any exceptions are caught and ignored.
7586          * @return {Roo.Element} this
7587          */
7588         blur : function() {
7589             try{
7590                 this.dom.blur();
7591             }catch(e){}
7592             return this;
7593         },
7594
7595         /**
7596          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7597          * @param {String/Array} className The CSS class to add, or an array of classes
7598          * @return {Roo.Element} this
7599          */
7600         addClass : function(className){
7601             if(className instanceof Array){
7602                 for(var i = 0, len = className.length; i < len; i++) {
7603                     this.addClass(className[i]);
7604                 }
7605             }else{
7606                 if(className && !this.hasClass(className)){
7607                     this.dom.className = this.dom.className + " " + className;
7608                 }
7609             }
7610             return this;
7611         },
7612
7613         /**
7614          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7615          * @param {String/Array} className The CSS class to add, or an array of classes
7616          * @return {Roo.Element} this
7617          */
7618         radioClass : function(className){
7619             var siblings = this.dom.parentNode.childNodes;
7620             for(var i = 0; i < siblings.length; i++) {
7621                 var s = siblings[i];
7622                 if(s.nodeType == 1){
7623                     Roo.get(s).removeClass(className);
7624                 }
7625             }
7626             this.addClass(className);
7627             return this;
7628         },
7629
7630         /**
7631          * Removes one or more CSS classes from the element.
7632          * @param {String/Array} className The CSS class to remove, or an array of classes
7633          * @return {Roo.Element} this
7634          */
7635         removeClass : function(className){
7636             if(!className || !this.dom.className){
7637                 return this;
7638             }
7639             if(className instanceof Array){
7640                 for(var i = 0, len = className.length; i < len; i++) {
7641                     this.removeClass(className[i]);
7642                 }
7643             }else{
7644                 if(this.hasClass(className)){
7645                     var re = this.classReCache[className];
7646                     if (!re) {
7647                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7648                        this.classReCache[className] = re;
7649                     }
7650                     this.dom.className =
7651                         this.dom.className.replace(re, " ");
7652                 }
7653             }
7654             return this;
7655         },
7656
7657         // private
7658         classReCache: {},
7659
7660         /**
7661          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7662          * @param {String} className The CSS class to toggle
7663          * @return {Roo.Element} this
7664          */
7665         toggleClass : function(className){
7666             if(this.hasClass(className)){
7667                 this.removeClass(className);
7668             }else{
7669                 this.addClass(className);
7670             }
7671             return this;
7672         },
7673
7674         /**
7675          * Checks if the specified CSS class exists on this element's DOM node.
7676          * @param {String} className The CSS class to check for
7677          * @return {Boolean} True if the class exists, else false
7678          */
7679         hasClass : function(className){
7680             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7681         },
7682
7683         /**
7684          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7685          * @param {String} oldClassName The CSS class to replace
7686          * @param {String} newClassName The replacement CSS class
7687          * @return {Roo.Element} this
7688          */
7689         replaceClass : function(oldClassName, newClassName){
7690             this.removeClass(oldClassName);
7691             this.addClass(newClassName);
7692             return this;
7693         },
7694
7695         /**
7696          * Returns an object with properties matching the styles requested.
7697          * For example, el.getStyles('color', 'font-size', 'width') might return
7698          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7699          * @param {String} style1 A style name
7700          * @param {String} style2 A style name
7701          * @param {String} etc.
7702          * @return {Object} The style object
7703          */
7704         getStyles : function(){
7705             var a = arguments, len = a.length, r = {};
7706             for(var i = 0; i < len; i++){
7707                 r[a[i]] = this.getStyle(a[i]);
7708             }
7709             return r;
7710         },
7711
7712         /**
7713          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7714          * @param {String} property The style property whose value is returned.
7715          * @return {String} The current value of the style property for this element.
7716          */
7717         getStyle : function(){
7718             return view && view.getComputedStyle ?
7719                 function(prop){
7720                     var el = this.dom, v, cs, camel;
7721                     if(prop == 'float'){
7722                         prop = "cssFloat";
7723                     }
7724                     if(el.style && (v = el.style[prop])){
7725                         return v;
7726                     }
7727                     if(cs = view.getComputedStyle(el, "")){
7728                         if(!(camel = propCache[prop])){
7729                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7730                         }
7731                         return cs[camel];
7732                     }
7733                     return null;
7734                 } :
7735                 function(prop){
7736                     var el = this.dom, v, cs, camel;
7737                     if(prop == 'opacity'){
7738                         if(typeof el.style.filter == 'string'){
7739                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7740                             if(m){
7741                                 var fv = parseFloat(m[1]);
7742                                 if(!isNaN(fv)){
7743                                     return fv ? fv / 100 : 0;
7744                                 }
7745                             }
7746                         }
7747                         return 1;
7748                     }else if(prop == 'float'){
7749                         prop = "styleFloat";
7750                     }
7751                     if(!(camel = propCache[prop])){
7752                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7753                     }
7754                     if(v = el.style[camel]){
7755                         return v;
7756                     }
7757                     if(cs = el.currentStyle){
7758                         return cs[camel];
7759                     }
7760                     return null;
7761                 };
7762         }(),
7763
7764         /**
7765          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7766          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7767          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7768          * @return {Roo.Element} this
7769          */
7770         setStyle : function(prop, value){
7771             if(typeof prop == "string"){
7772                 
7773                 if (prop == 'float') {
7774                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7775                     return this;
7776                 }
7777                 
7778                 var camel;
7779                 if(!(camel = propCache[prop])){
7780                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7781                 }
7782                 
7783                 if(camel == 'opacity') {
7784                     this.setOpacity(value);
7785                 }else{
7786                     this.dom.style[camel] = value;
7787                 }
7788             }else{
7789                 for(var style in prop){
7790                     if(typeof prop[style] != "function"){
7791                        this.setStyle(style, prop[style]);
7792                     }
7793                 }
7794             }
7795             return this;
7796         },
7797
7798         /**
7799          * More flexible version of {@link #setStyle} for setting style properties.
7800          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7801          * a function which returns such a specification.
7802          * @return {Roo.Element} this
7803          */
7804         applyStyles : function(style){
7805             Roo.DomHelper.applyStyles(this.dom, style);
7806             return this;
7807         },
7808
7809         /**
7810           * 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).
7811           * @return {Number} The X position of the element
7812           */
7813         getX : function(){
7814             return D.getX(this.dom);
7815         },
7816
7817         /**
7818           * 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).
7819           * @return {Number} The Y position of the element
7820           */
7821         getY : function(){
7822             return D.getY(this.dom);
7823         },
7824
7825         /**
7826           * 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).
7827           * @return {Array} The XY position of the element
7828           */
7829         getXY : function(){
7830             return D.getXY(this.dom);
7831         },
7832
7833         /**
7834          * 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).
7835          * @param {Number} The X position of the element
7836          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7837          * @return {Roo.Element} this
7838          */
7839         setX : function(x, animate){
7840             if(!animate || !A){
7841                 D.setX(this.dom, x);
7842             }else{
7843                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7844             }
7845             return this;
7846         },
7847
7848         /**
7849          * 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).
7850          * @param {Number} The Y position of the element
7851          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7852          * @return {Roo.Element} this
7853          */
7854         setY : function(y, animate){
7855             if(!animate || !A){
7856                 D.setY(this.dom, y);
7857             }else{
7858                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7859             }
7860             return this;
7861         },
7862
7863         /**
7864          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7865          * @param {String} left The left CSS property value
7866          * @return {Roo.Element} this
7867          */
7868         setLeft : function(left){
7869             this.setStyle("left", this.addUnits(left));
7870             return this;
7871         },
7872
7873         /**
7874          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7875          * @param {String} top The top CSS property value
7876          * @return {Roo.Element} this
7877          */
7878         setTop : function(top){
7879             this.setStyle("top", this.addUnits(top));
7880             return this;
7881         },
7882
7883         /**
7884          * Sets the element's CSS right style.
7885          * @param {String} right The right CSS property value
7886          * @return {Roo.Element} this
7887          */
7888         setRight : function(right){
7889             this.setStyle("right", this.addUnits(right));
7890             return this;
7891         },
7892
7893         /**
7894          * Sets the element's CSS bottom style.
7895          * @param {String} bottom The bottom CSS property value
7896          * @return {Roo.Element} this
7897          */
7898         setBottom : function(bottom){
7899             this.setStyle("bottom", this.addUnits(bottom));
7900             return this;
7901         },
7902
7903         /**
7904          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7905          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7906          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7907          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7908          * @return {Roo.Element} this
7909          */
7910         setXY : function(pos, animate){
7911             if(!animate || !A){
7912                 D.setXY(this.dom, pos);
7913             }else{
7914                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7915             }
7916             return this;
7917         },
7918
7919         /**
7920          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7921          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7922          * @param {Number} x X value for new position (coordinates are page-based)
7923          * @param {Number} y Y value for new position (coordinates are page-based)
7924          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7925          * @return {Roo.Element} this
7926          */
7927         setLocation : function(x, y, animate){
7928             this.setXY([x, y], this.preanim(arguments, 2));
7929             return this;
7930         },
7931
7932         /**
7933          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7934          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7935          * @param {Number} x X value for new position (coordinates are page-based)
7936          * @param {Number} y Y value for new position (coordinates are page-based)
7937          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7938          * @return {Roo.Element} this
7939          */
7940         moveTo : function(x, y, animate){
7941             this.setXY([x, y], this.preanim(arguments, 2));
7942             return this;
7943         },
7944
7945         /**
7946          * Returns the region of the given element.
7947          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7948          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7949          */
7950         getRegion : function(){
7951             return D.getRegion(this.dom);
7952         },
7953
7954         /**
7955          * Returns the offset height of the element
7956          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7957          * @return {Number} The element's height
7958          */
7959         getHeight : function(contentHeight){
7960             var h = this.dom.offsetHeight || 0;
7961             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7962         },
7963
7964         /**
7965          * Returns the offset width of the element
7966          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7967          * @return {Number} The element's width
7968          */
7969         getWidth : function(contentWidth){
7970             var w = this.dom.offsetWidth || 0;
7971             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7972         },
7973
7974         /**
7975          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7976          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7977          * if a height has not been set using CSS.
7978          * @return {Number}
7979          */
7980         getComputedHeight : function(){
7981             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7982             if(!h){
7983                 h = parseInt(this.getStyle('height'), 10) || 0;
7984                 if(!this.isBorderBox()){
7985                     h += this.getFrameWidth('tb');
7986                 }
7987             }
7988             return h;
7989         },
7990
7991         /**
7992          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7993          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7994          * if a width has not been set using CSS.
7995          * @return {Number}
7996          */
7997         getComputedWidth : function(){
7998             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7999             if(!w){
8000                 w = parseInt(this.getStyle('width'), 10) || 0;
8001                 if(!this.isBorderBox()){
8002                     w += this.getFrameWidth('lr');
8003                 }
8004             }
8005             return w;
8006         },
8007
8008         /**
8009          * Returns the size of the element.
8010          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8011          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8012          */
8013         getSize : function(contentSize){
8014             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8015         },
8016
8017         /**
8018          * Returns the width and height of the viewport.
8019          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8020          */
8021         getViewSize : function(){
8022             var d = this.dom, doc = document, aw = 0, ah = 0;
8023             if(d == doc || d == doc.body){
8024                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8025             }else{
8026                 return {
8027                     width : d.clientWidth,
8028                     height: d.clientHeight
8029                 };
8030             }
8031         },
8032
8033         /**
8034          * Returns the value of the "value" attribute
8035          * @param {Boolean} asNumber true to parse the value as a number
8036          * @return {String/Number}
8037          */
8038         getValue : function(asNumber){
8039             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8040         },
8041
8042         // private
8043         adjustWidth : function(width){
8044             if(typeof width == "number"){
8045                 if(this.autoBoxAdjust && !this.isBorderBox()){
8046                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8047                 }
8048                 if(width < 0){
8049                     width = 0;
8050                 }
8051             }
8052             return width;
8053         },
8054
8055         // private
8056         adjustHeight : function(height){
8057             if(typeof height == "number"){
8058                if(this.autoBoxAdjust && !this.isBorderBox()){
8059                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8060                }
8061                if(height < 0){
8062                    height = 0;
8063                }
8064             }
8065             return height;
8066         },
8067
8068         /**
8069          * Set the width of the element
8070          * @param {Number} width The new width
8071          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8072          * @return {Roo.Element} this
8073          */
8074         setWidth : function(width, animate){
8075             width = this.adjustWidth(width);
8076             if(!animate || !A){
8077                 this.dom.style.width = this.addUnits(width);
8078             }else{
8079                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8080             }
8081             return this;
8082         },
8083
8084         /**
8085          * Set the height of the element
8086          * @param {Number} height The new height
8087          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8088          * @return {Roo.Element} this
8089          */
8090          setHeight : function(height, animate){
8091             height = this.adjustHeight(height);
8092             if(!animate || !A){
8093                 this.dom.style.height = this.addUnits(height);
8094             }else{
8095                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8096             }
8097             return this;
8098         },
8099
8100         /**
8101          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8102          * @param {Number} width The new width
8103          * @param {Number} height The new height
8104          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8105          * @return {Roo.Element} this
8106          */
8107          setSize : function(width, height, animate){
8108             if(typeof width == "object"){ // in case of object from getSize()
8109                 height = width.height; width = width.width;
8110             }
8111             width = this.adjustWidth(width); height = this.adjustHeight(height);
8112             if(!animate || !A){
8113                 this.dom.style.width = this.addUnits(width);
8114                 this.dom.style.height = this.addUnits(height);
8115             }else{
8116                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8117             }
8118             return this;
8119         },
8120
8121         /**
8122          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8123          * @param {Number} x X value for new position (coordinates are page-based)
8124          * @param {Number} y Y value for new position (coordinates are page-based)
8125          * @param {Number} width The new width
8126          * @param {Number} height The new height
8127          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8128          * @return {Roo.Element} this
8129          */
8130         setBounds : function(x, y, width, height, animate){
8131             if(!animate || !A){
8132                 this.setSize(width, height);
8133                 this.setLocation(x, y);
8134             }else{
8135                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8136                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8137                               this.preanim(arguments, 4), 'motion');
8138             }
8139             return this;
8140         },
8141
8142         /**
8143          * 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.
8144          * @param {Roo.lib.Region} region The region to fill
8145          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8146          * @return {Roo.Element} this
8147          */
8148         setRegion : function(region, animate){
8149             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8150             return this;
8151         },
8152
8153         /**
8154          * Appends an event handler
8155          *
8156          * @param {String}   eventName     The type of event to append
8157          * @param {Function} fn        The method the event invokes
8158          * @param {Object} scope       (optional) The scope (this object) of the fn
8159          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8160          */
8161         addListener : function(eventName, fn, scope, options){
8162             if (this.dom) {
8163                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8164             }
8165         },
8166
8167         /**
8168          * Removes an event handler from this element
8169          * @param {String} eventName the type of event to remove
8170          * @param {Function} fn the method the event invokes
8171          * @return {Roo.Element} this
8172          */
8173         removeListener : function(eventName, fn){
8174             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8175             return this;
8176         },
8177
8178         /**
8179          * Removes all previous added listeners from this element
8180          * @return {Roo.Element} this
8181          */
8182         removeAllListeners : function(){
8183             E.purgeElement(this.dom);
8184             return this;
8185         },
8186
8187         relayEvent : function(eventName, observable){
8188             this.on(eventName, function(e){
8189                 observable.fireEvent(eventName, e);
8190             });
8191         },
8192
8193         /**
8194          * Set the opacity of the element
8195          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8196          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8197          * @return {Roo.Element} this
8198          */
8199          setOpacity : function(opacity, animate){
8200             if(!animate || !A){
8201                 var s = this.dom.style;
8202                 if(Roo.isIE){
8203                     s.zoom = 1;
8204                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8205                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8206                 }else{
8207                     s.opacity = opacity;
8208                 }
8209             }else{
8210                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8211             }
8212             return this;
8213         },
8214
8215         /**
8216          * Gets the left X coordinate
8217          * @param {Boolean} local True to get the local css position instead of page coordinate
8218          * @return {Number}
8219          */
8220         getLeft : function(local){
8221             if(!local){
8222                 return this.getX();
8223             }else{
8224                 return parseInt(this.getStyle("left"), 10) || 0;
8225             }
8226         },
8227
8228         /**
8229          * Gets the right X coordinate of the element (element X position + element width)
8230          * @param {Boolean} local True to get the local css position instead of page coordinate
8231          * @return {Number}
8232          */
8233         getRight : function(local){
8234             if(!local){
8235                 return this.getX() + this.getWidth();
8236             }else{
8237                 return (this.getLeft(true) + this.getWidth()) || 0;
8238             }
8239         },
8240
8241         /**
8242          * Gets the top Y coordinate
8243          * @param {Boolean} local True to get the local css position instead of page coordinate
8244          * @return {Number}
8245          */
8246         getTop : function(local) {
8247             if(!local){
8248                 return this.getY();
8249             }else{
8250                 return parseInt(this.getStyle("top"), 10) || 0;
8251             }
8252         },
8253
8254         /**
8255          * Gets the bottom Y coordinate of the element (element Y position + element height)
8256          * @param {Boolean} local True to get the local css position instead of page coordinate
8257          * @return {Number}
8258          */
8259         getBottom : function(local){
8260             if(!local){
8261                 return this.getY() + this.getHeight();
8262             }else{
8263                 return (this.getTop(true) + this.getHeight()) || 0;
8264             }
8265         },
8266
8267         /**
8268         * Initializes positioning on this element. If a desired position is not passed, it will make the
8269         * the element positioned relative IF it is not already positioned.
8270         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8271         * @param {Number} zIndex (optional) The zIndex to apply
8272         * @param {Number} x (optional) Set the page X position
8273         * @param {Number} y (optional) Set the page Y position
8274         */
8275         position : function(pos, zIndex, x, y){
8276             if(!pos){
8277                if(this.getStyle('position') == 'static'){
8278                    this.setStyle('position', 'relative');
8279                }
8280             }else{
8281                 this.setStyle("position", pos);
8282             }
8283             if(zIndex){
8284                 this.setStyle("z-index", zIndex);
8285             }
8286             if(x !== undefined && y !== undefined){
8287                 this.setXY([x, y]);
8288             }else if(x !== undefined){
8289                 this.setX(x);
8290             }else if(y !== undefined){
8291                 this.setY(y);
8292             }
8293         },
8294
8295         /**
8296         * Clear positioning back to the default when the document was loaded
8297         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8298         * @return {Roo.Element} this
8299          */
8300         clearPositioning : function(value){
8301             value = value ||'';
8302             this.setStyle({
8303                 "left": value,
8304                 "right": value,
8305                 "top": value,
8306                 "bottom": value,
8307                 "z-index": "",
8308                 "position" : "static"
8309             });
8310             return this;
8311         },
8312
8313         /**
8314         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8315         * snapshot before performing an update and then restoring the element.
8316         * @return {Object}
8317         */
8318         getPositioning : function(){
8319             var l = this.getStyle("left");
8320             var t = this.getStyle("top");
8321             return {
8322                 "position" : this.getStyle("position"),
8323                 "left" : l,
8324                 "right" : l ? "" : this.getStyle("right"),
8325                 "top" : t,
8326                 "bottom" : t ? "" : this.getStyle("bottom"),
8327                 "z-index" : this.getStyle("z-index")
8328             };
8329         },
8330
8331         /**
8332          * Gets the width of the border(s) for the specified side(s)
8333          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8334          * passing lr would get the border (l)eft width + the border (r)ight width.
8335          * @return {Number} The width of the sides passed added together
8336          */
8337         getBorderWidth : function(side){
8338             return this.addStyles(side, El.borders);
8339         },
8340
8341         /**
8342          * Gets the width of the padding(s) for the specified side(s)
8343          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8344          * passing lr would get the padding (l)eft + the padding (r)ight.
8345          * @return {Number} The padding of the sides passed added together
8346          */
8347         getPadding : function(side){
8348             return this.addStyles(side, El.paddings);
8349         },
8350
8351         /**
8352         * Set positioning with an object returned by getPositioning().
8353         * @param {Object} posCfg
8354         * @return {Roo.Element} this
8355          */
8356         setPositioning : function(pc){
8357             this.applyStyles(pc);
8358             if(pc.right == "auto"){
8359                 this.dom.style.right = "";
8360             }
8361             if(pc.bottom == "auto"){
8362                 this.dom.style.bottom = "";
8363             }
8364             return this;
8365         },
8366
8367         // private
8368         fixDisplay : function(){
8369             if(this.getStyle("display") == "none"){
8370                 this.setStyle("visibility", "hidden");
8371                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8372                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8373                     this.setStyle("display", "block");
8374                 }
8375             }
8376         },
8377
8378         /**
8379          * Quick set left and top adding default units
8380          * @param {String} left The left CSS property value
8381          * @param {String} top The top CSS property value
8382          * @return {Roo.Element} this
8383          */
8384          setLeftTop : function(left, top){
8385             this.dom.style.left = this.addUnits(left);
8386             this.dom.style.top = this.addUnits(top);
8387             return this;
8388         },
8389
8390         /**
8391          * Move this element relative to its current position.
8392          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8393          * @param {Number} distance How far to move the element in pixels
8394          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8395          * @return {Roo.Element} this
8396          */
8397          move : function(direction, distance, animate){
8398             var xy = this.getXY();
8399             direction = direction.toLowerCase();
8400             switch(direction){
8401                 case "l":
8402                 case "left":
8403                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8404                     break;
8405                case "r":
8406                case "right":
8407                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8408                     break;
8409                case "t":
8410                case "top":
8411                case "up":
8412                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8413                     break;
8414                case "b":
8415                case "bottom":
8416                case "down":
8417                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8418                     break;
8419             }
8420             return this;
8421         },
8422
8423         /**
8424          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8425          * @return {Roo.Element} this
8426          */
8427         clip : function(){
8428             if(!this.isClipped){
8429                this.isClipped = true;
8430                this.originalClip = {
8431                    "o": this.getStyle("overflow"),
8432                    "x": this.getStyle("overflow-x"),
8433                    "y": this.getStyle("overflow-y")
8434                };
8435                this.setStyle("overflow", "hidden");
8436                this.setStyle("overflow-x", "hidden");
8437                this.setStyle("overflow-y", "hidden");
8438             }
8439             return this;
8440         },
8441
8442         /**
8443          *  Return clipping (overflow) to original clipping before clip() was called
8444          * @return {Roo.Element} this
8445          */
8446         unclip : function(){
8447             if(this.isClipped){
8448                 this.isClipped = false;
8449                 var o = this.originalClip;
8450                 if(o.o){this.setStyle("overflow", o.o);}
8451                 if(o.x){this.setStyle("overflow-x", o.x);}
8452                 if(o.y){this.setStyle("overflow-y", o.y);}
8453             }
8454             return this;
8455         },
8456
8457
8458         /**
8459          * Gets the x,y coordinates specified by the anchor position on the element.
8460          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8461          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8462          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8463          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8464          * @return {Array} [x, y] An array containing the element's x and y coordinates
8465          */
8466         getAnchorXY : function(anchor, local, s){
8467             //Passing a different size is useful for pre-calculating anchors,
8468             //especially for anchored animations that change the el size.
8469
8470             var w, h, vp = false;
8471             if(!s){
8472                 var d = this.dom;
8473                 if(d == document.body || d == document){
8474                     vp = true;
8475                     w = D.getViewWidth(); h = D.getViewHeight();
8476                 }else{
8477                     w = this.getWidth(); h = this.getHeight();
8478                 }
8479             }else{
8480                 w = s.width;  h = s.height;
8481             }
8482             var x = 0, y = 0, r = Math.round;
8483             switch((anchor || "tl").toLowerCase()){
8484                 case "c":
8485                     x = r(w*.5);
8486                     y = r(h*.5);
8487                 break;
8488                 case "t":
8489                     x = r(w*.5);
8490                     y = 0;
8491                 break;
8492                 case "l":
8493                     x = 0;
8494                     y = r(h*.5);
8495                 break;
8496                 case "r":
8497                     x = w;
8498                     y = r(h*.5);
8499                 break;
8500                 case "b":
8501                     x = r(w*.5);
8502                     y = h;
8503                 break;
8504                 case "tl":
8505                     x = 0;
8506                     y = 0;
8507                 break;
8508                 case "bl":
8509                     x = 0;
8510                     y = h;
8511                 break;
8512                 case "br":
8513                     x = w;
8514                     y = h;
8515                 break;
8516                 case "tr":
8517                     x = w;
8518                     y = 0;
8519                 break;
8520             }
8521             if(local === true){
8522                 return [x, y];
8523             }
8524             if(vp){
8525                 var sc = this.getScroll();
8526                 return [x + sc.left, y + sc.top];
8527             }
8528             //Add the element's offset xy
8529             var o = this.getXY();
8530             return [x+o[0], y+o[1]];
8531         },
8532
8533         /**
8534          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8535          * supported position values.
8536          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8537          * @param {String} position The position to align to.
8538          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8539          * @return {Array} [x, y]
8540          */
8541         getAlignToXY : function(el, p, o){
8542             el = Roo.get(el);
8543             var d = this.dom;
8544             if(!el.dom){
8545                 throw "Element.alignTo with an element that doesn't exist";
8546             }
8547             var c = false; //constrain to viewport
8548             var p1 = "", p2 = "";
8549             o = o || [0,0];
8550
8551             if(!p){
8552                 p = "tl-bl";
8553             }else if(p == "?"){
8554                 p = "tl-bl?";
8555             }else if(p.indexOf("-") == -1){
8556                 p = "tl-" + p;
8557             }
8558             p = p.toLowerCase();
8559             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8560             if(!m){
8561                throw "Element.alignTo with an invalid alignment " + p;
8562             }
8563             p1 = m[1]; p2 = m[2]; c = !!m[3];
8564
8565             //Subtract the aligned el's internal xy from the target's offset xy
8566             //plus custom offset to get the aligned el's new offset xy
8567             var a1 = this.getAnchorXY(p1, true);
8568             var a2 = el.getAnchorXY(p2, false);
8569             var x = a2[0] - a1[0] + o[0];
8570             var y = a2[1] - a1[1] + o[1];
8571             if(c){
8572                 //constrain the aligned el to viewport if necessary
8573                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8574                 // 5px of margin for ie
8575                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8576
8577                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8578                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8579                 //otherwise swap the aligned el to the opposite border of the target.
8580                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8581                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8582                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8583                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8584
8585                var doc = document;
8586                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8587                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8588
8589                if((x+w) > dw + scrollX){
8590                     x = swapX ? r.left-w : dw+scrollX-w;
8591                 }
8592                if(x < scrollX){
8593                    x = swapX ? r.right : scrollX;
8594                }
8595                if((y+h) > dh + scrollY){
8596                     y = swapY ? r.top-h : dh+scrollY-h;
8597                 }
8598                if (y < scrollY){
8599                    y = swapY ? r.bottom : scrollY;
8600                }
8601             }
8602             return [x,y];
8603         },
8604
8605         // private
8606         getConstrainToXY : function(){
8607             var os = {top:0, left:0, bottom:0, right: 0};
8608
8609             return function(el, local, offsets, proposedXY){
8610                 el = Roo.get(el);
8611                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8612
8613                 var vw, vh, vx = 0, vy = 0;
8614                 if(el.dom == document.body || el.dom == document){
8615                     vw = Roo.lib.Dom.getViewWidth();
8616                     vh = Roo.lib.Dom.getViewHeight();
8617                 }else{
8618                     vw = el.dom.clientWidth;
8619                     vh = el.dom.clientHeight;
8620                     if(!local){
8621                         var vxy = el.getXY();
8622                         vx = vxy[0];
8623                         vy = vxy[1];
8624                     }
8625                 }
8626
8627                 var s = el.getScroll();
8628
8629                 vx += offsets.left + s.left;
8630                 vy += offsets.top + s.top;
8631
8632                 vw -= offsets.right;
8633                 vh -= offsets.bottom;
8634
8635                 var vr = vx+vw;
8636                 var vb = vy+vh;
8637
8638                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8639                 var x = xy[0], y = xy[1];
8640                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8641
8642                 // only move it if it needs it
8643                 var moved = false;
8644
8645                 // first validate right/bottom
8646                 if((x + w) > vr){
8647                     x = vr - w;
8648                     moved = true;
8649                 }
8650                 if((y + h) > vb){
8651                     y = vb - h;
8652                     moved = true;
8653                 }
8654                 // then make sure top/left isn't negative
8655                 if(x < vx){
8656                     x = vx;
8657                     moved = true;
8658                 }
8659                 if(y < vy){
8660                     y = vy;
8661                     moved = true;
8662                 }
8663                 return moved ? [x, y] : false;
8664             };
8665         }(),
8666
8667         // private
8668         adjustForConstraints : function(xy, parent, offsets){
8669             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8670         },
8671
8672         /**
8673          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8674          * document it aligns it to the viewport.
8675          * The position parameter is optional, and can be specified in any one of the following formats:
8676          * <ul>
8677          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8678          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8679          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8680          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8681          *   <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
8682          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8683          * </ul>
8684          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8685          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8686          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8687          * that specified in order to enforce the viewport constraints.
8688          * Following are all of the supported anchor positions:
8689     <pre>
8690     Value  Description
8691     -----  -----------------------------
8692     tl     The top left corner (default)
8693     t      The center of the top edge
8694     tr     The top right corner
8695     l      The center of the left edge
8696     c      In the center of the element
8697     r      The center of the right edge
8698     bl     The bottom left corner
8699     b      The center of the bottom edge
8700     br     The bottom right corner
8701     </pre>
8702     Example Usage:
8703     <pre><code>
8704     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8705     el.alignTo("other-el");
8706
8707     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8708     el.alignTo("other-el", "tr?");
8709
8710     // align the bottom right corner of el with the center left edge of other-el
8711     el.alignTo("other-el", "br-l?");
8712
8713     // align the center of el with the bottom left corner of other-el and
8714     // adjust the x position by -6 pixels (and the y position by 0)
8715     el.alignTo("other-el", "c-bl", [-6, 0]);
8716     </code></pre>
8717          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8718          * @param {String} position The position to align to.
8719          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8720          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8721          * @return {Roo.Element} this
8722          */
8723         alignTo : function(element, position, offsets, animate){
8724             var xy = this.getAlignToXY(element, position, offsets);
8725             this.setXY(xy, this.preanim(arguments, 3));
8726             return this;
8727         },
8728
8729         /**
8730          * Anchors an element to another element and realigns it when the window is resized.
8731          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8732          * @param {String} position The position to align to.
8733          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8734          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8735          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8736          * is a number, it is used as the buffer delay (defaults to 50ms).
8737          * @param {Function} callback The function to call after the animation finishes
8738          * @return {Roo.Element} this
8739          */
8740         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8741             var action = function(){
8742                 this.alignTo(el, alignment, offsets, animate);
8743                 Roo.callback(callback, this);
8744             };
8745             Roo.EventManager.onWindowResize(action, this);
8746             var tm = typeof monitorScroll;
8747             if(tm != 'undefined'){
8748                 Roo.EventManager.on(window, 'scroll', action, this,
8749                     {buffer: tm == 'number' ? monitorScroll : 50});
8750             }
8751             action.call(this); // align immediately
8752             return this;
8753         },
8754         /**
8755          * Clears any opacity settings from this element. Required in some cases for IE.
8756          * @return {Roo.Element} this
8757          */
8758         clearOpacity : function(){
8759             if (window.ActiveXObject) {
8760                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8761                     this.dom.style.filter = "";
8762                 }
8763             } else {
8764                 this.dom.style.opacity = "";
8765                 this.dom.style["-moz-opacity"] = "";
8766                 this.dom.style["-khtml-opacity"] = "";
8767             }
8768             return this;
8769         },
8770
8771         /**
8772          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8773          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8774          * @return {Roo.Element} this
8775          */
8776         hide : function(animate){
8777             this.setVisible(false, this.preanim(arguments, 0));
8778             return this;
8779         },
8780
8781         /**
8782         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8783         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8784          * @return {Roo.Element} this
8785          */
8786         show : function(animate){
8787             this.setVisible(true, this.preanim(arguments, 0));
8788             return this;
8789         },
8790
8791         /**
8792          * @private Test if size has a unit, otherwise appends the default
8793          */
8794         addUnits : function(size){
8795             return Roo.Element.addUnits(size, this.defaultUnit);
8796         },
8797
8798         /**
8799          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8800          * @return {Roo.Element} this
8801          */
8802         beginMeasure : function(){
8803             var el = this.dom;
8804             if(el.offsetWidth || el.offsetHeight){
8805                 return this; // offsets work already
8806             }
8807             var changed = [];
8808             var p = this.dom, b = document.body; // start with this element
8809             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8810                 var pe = Roo.get(p);
8811                 if(pe.getStyle('display') == 'none'){
8812                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8813                     p.style.visibility = "hidden";
8814                     p.style.display = "block";
8815                 }
8816                 p = p.parentNode;
8817             }
8818             this._measureChanged = changed;
8819             return this;
8820
8821         },
8822
8823         /**
8824          * Restores displays to before beginMeasure was called
8825          * @return {Roo.Element} this
8826          */
8827         endMeasure : function(){
8828             var changed = this._measureChanged;
8829             if(changed){
8830                 for(var i = 0, len = changed.length; i < len; i++) {
8831                     var r = changed[i];
8832                     r.el.style.visibility = r.visibility;
8833                     r.el.style.display = "none";
8834                 }
8835                 this._measureChanged = null;
8836             }
8837             return this;
8838         },
8839
8840         /**
8841         * Update the innerHTML of this element, optionally searching for and processing scripts
8842         * @param {String} html The new HTML
8843         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8844         * @param {Function} callback For async script loading you can be noticed when the update completes
8845         * @return {Roo.Element} this
8846          */
8847         update : function(html, loadScripts, callback){
8848             if(typeof html == "undefined"){
8849                 html = "";
8850             }
8851             if(loadScripts !== true){
8852                 this.dom.innerHTML = html;
8853                 if(typeof callback == "function"){
8854                     callback();
8855                 }
8856                 return this;
8857             }
8858             var id = Roo.id();
8859             var dom = this.dom;
8860
8861             html += '<span id="' + id + '"></span>';
8862
8863             E.onAvailable(id, function(){
8864                 var hd = document.getElementsByTagName("head")[0];
8865                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8866                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8867                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8868
8869                 var match;
8870                 while(match = re.exec(html)){
8871                     var attrs = match[1];
8872                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8873                     if(srcMatch && srcMatch[2]){
8874                        var s = document.createElement("script");
8875                        s.src = srcMatch[2];
8876                        var typeMatch = attrs.match(typeRe);
8877                        if(typeMatch && typeMatch[2]){
8878                            s.type = typeMatch[2];
8879                        }
8880                        hd.appendChild(s);
8881                     }else if(match[2] && match[2].length > 0){
8882                         if(window.execScript) {
8883                            window.execScript(match[2]);
8884                         } else {
8885                             /**
8886                              * eval:var:id
8887                              * eval:var:dom
8888                              * eval:var:html
8889                              * 
8890                              */
8891                            window.eval(match[2]);
8892                         }
8893                     }
8894                 }
8895                 var el = document.getElementById(id);
8896                 if(el){el.parentNode.removeChild(el);}
8897                 if(typeof callback == "function"){
8898                     callback();
8899                 }
8900             });
8901             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8902             return this;
8903         },
8904
8905         /**
8906          * Direct access to the UpdateManager update() method (takes the same parameters).
8907          * @param {String/Function} url The url for this request or a function to call to get the url
8908          * @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}
8909          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8910          * @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.
8911          * @return {Roo.Element} this
8912          */
8913         load : function(){
8914             var um = this.getUpdateManager();
8915             um.update.apply(um, arguments);
8916             return this;
8917         },
8918
8919         /**
8920         * Gets this element's UpdateManager
8921         * @return {Roo.UpdateManager} The UpdateManager
8922         */
8923         getUpdateManager : function(){
8924             if(!this.updateManager){
8925                 this.updateManager = new Roo.UpdateManager(this);
8926             }
8927             return this.updateManager;
8928         },
8929
8930         /**
8931          * Disables text selection for this element (normalized across browsers)
8932          * @return {Roo.Element} this
8933          */
8934         unselectable : function(){
8935             this.dom.unselectable = "on";
8936             this.swallowEvent("selectstart", true);
8937             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8938             this.addClass("x-unselectable");
8939             return this;
8940         },
8941
8942         /**
8943         * Calculates the x, y to center this element on the screen
8944         * @return {Array} The x, y values [x, y]
8945         */
8946         getCenterXY : function(){
8947             return this.getAlignToXY(document, 'c-c');
8948         },
8949
8950         /**
8951         * Centers the Element in either the viewport, or another Element.
8952         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8953         */
8954         center : function(centerIn){
8955             this.alignTo(centerIn || document, 'c-c');
8956             return this;
8957         },
8958
8959         /**
8960          * Tests various css rules/browsers to determine if this element uses a border box
8961          * @return {Boolean}
8962          */
8963         isBorderBox : function(){
8964             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8965         },
8966
8967         /**
8968          * Return a box {x, y, width, height} that can be used to set another elements
8969          * size/location to match this element.
8970          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8971          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8972          * @return {Object} box An object in the format {x, y, width, height}
8973          */
8974         getBox : function(contentBox, local){
8975             var xy;
8976             if(!local){
8977                 xy = this.getXY();
8978             }else{
8979                 var left = parseInt(this.getStyle("left"), 10) || 0;
8980                 var top = parseInt(this.getStyle("top"), 10) || 0;
8981                 xy = [left, top];
8982             }
8983             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8984             if(!contentBox){
8985                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8986             }else{
8987                 var l = this.getBorderWidth("l")+this.getPadding("l");
8988                 var r = this.getBorderWidth("r")+this.getPadding("r");
8989                 var t = this.getBorderWidth("t")+this.getPadding("t");
8990                 var b = this.getBorderWidth("b")+this.getPadding("b");
8991                 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)};
8992             }
8993             bx.right = bx.x + bx.width;
8994             bx.bottom = bx.y + bx.height;
8995             return bx;
8996         },
8997
8998         /**
8999          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9000          for more information about the sides.
9001          * @param {String} sides
9002          * @return {Number}
9003          */
9004         getFrameWidth : function(sides, onlyContentBox){
9005             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9006         },
9007
9008         /**
9009          * 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.
9010          * @param {Object} box The box to fill {x, y, width, height}
9011          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9012          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9013          * @return {Roo.Element} this
9014          */
9015         setBox : function(box, adjust, animate){
9016             var w = box.width, h = box.height;
9017             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9018                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9019                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9020             }
9021             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9022             return this;
9023         },
9024
9025         /**
9026          * Forces the browser to repaint this element
9027          * @return {Roo.Element} this
9028          */
9029          repaint : function(){
9030             var dom = this.dom;
9031             this.addClass("x-repaint");
9032             setTimeout(function(){
9033                 Roo.get(dom).removeClass("x-repaint");
9034             }, 1);
9035             return this;
9036         },
9037
9038         /**
9039          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9040          * then it returns the calculated width of the sides (see getPadding)
9041          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9042          * @return {Object/Number}
9043          */
9044         getMargins : function(side){
9045             if(!side){
9046                 return {
9047                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9048                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9049                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9050                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9051                 };
9052             }else{
9053                 return this.addStyles(side, El.margins);
9054              }
9055         },
9056
9057         // private
9058         addStyles : function(sides, styles){
9059             var val = 0, v, w;
9060             for(var i = 0, len = sides.length; i < len; i++){
9061                 v = this.getStyle(styles[sides.charAt(i)]);
9062                 if(v){
9063                      w = parseInt(v, 10);
9064                      if(w){ val += w; }
9065                 }
9066             }
9067             return val;
9068         },
9069
9070         /**
9071          * Creates a proxy element of this element
9072          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9073          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9074          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9075          * @return {Roo.Element} The new proxy element
9076          */
9077         createProxy : function(config, renderTo, matchBox){
9078             if(renderTo){
9079                 renderTo = Roo.getDom(renderTo);
9080             }else{
9081                 renderTo = document.body;
9082             }
9083             config = typeof config == "object" ?
9084                 config : {tag : "div", cls: config};
9085             var proxy = Roo.DomHelper.append(renderTo, config, true);
9086             if(matchBox){
9087                proxy.setBox(this.getBox());
9088             }
9089             return proxy;
9090         },
9091
9092         /**
9093          * Puts a mask over this element to disable user interaction. Requires core.css.
9094          * This method can only be applied to elements which accept child nodes.
9095          * @param {String} msg (optional) A message to display in the mask
9096          * @param {String} msgCls (optional) A css class to apply to the msg element
9097          * @return {Element} The mask  element
9098          */
9099         mask : function(msg, msgCls)
9100         {
9101             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9102                 this.setStyle("position", "relative");
9103             }
9104             if(!this._mask){
9105                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9106             }
9107             this.addClass("x-masked");
9108             this._mask.setDisplayed(true);
9109             
9110             // we wander
9111             var z = 0;
9112             var dom = this.dom;
9113             while (dom && dom.style) {
9114                 if (!isNaN(parseInt(dom.style.zIndex))) {
9115                     z = Math.max(z, parseInt(dom.style.zIndex));
9116                 }
9117                 dom = dom.parentNode;
9118             }
9119             // if we are masking the body - then it hides everything..
9120             if (this.dom == document.body) {
9121                 z = 1000000;
9122                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9123                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9124             }
9125            
9126             if(typeof msg == 'string'){
9127                 if(!this._maskMsg){
9128                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9129                 }
9130                 var mm = this._maskMsg;
9131                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9132                 if (mm.dom.firstChild) { // weird IE issue?
9133                     mm.dom.firstChild.innerHTML = msg;
9134                 }
9135                 mm.setDisplayed(true);
9136                 mm.center(this);
9137                 mm.setStyle('z-index', z + 102);
9138             }
9139             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9140                 this._mask.setHeight(this.getHeight());
9141             }
9142             this._mask.setStyle('z-index', z + 100);
9143             
9144             return this._mask;
9145         },
9146
9147         /**
9148          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9149          * it is cached for reuse.
9150          */
9151         unmask : function(removeEl){
9152             if(this._mask){
9153                 if(removeEl === true){
9154                     this._mask.remove();
9155                     delete this._mask;
9156                     if(this._maskMsg){
9157                         this._maskMsg.remove();
9158                         delete this._maskMsg;
9159                     }
9160                 }else{
9161                     this._mask.setDisplayed(false);
9162                     if(this._maskMsg){
9163                         this._maskMsg.setDisplayed(false);
9164                     }
9165                 }
9166             }
9167             this.removeClass("x-masked");
9168         },
9169
9170         /**
9171          * Returns true if this element is masked
9172          * @return {Boolean}
9173          */
9174         isMasked : function(){
9175             return this._mask && this._mask.isVisible();
9176         },
9177
9178         /**
9179          * Creates an iframe shim for this element to keep selects and other windowed objects from
9180          * showing through.
9181          * @return {Roo.Element} The new shim element
9182          */
9183         createShim : function(){
9184             var el = document.createElement('iframe');
9185             el.frameBorder = 'no';
9186             el.className = 'roo-shim';
9187             if(Roo.isIE && Roo.isSecure){
9188                 el.src = Roo.SSL_SECURE_URL;
9189             }
9190             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9191             shim.autoBoxAdjust = false;
9192             return shim;
9193         },
9194
9195         /**
9196          * Removes this element from the DOM and deletes it from the cache
9197          */
9198         remove : function(){
9199             if(this.dom.parentNode){
9200                 this.dom.parentNode.removeChild(this.dom);
9201             }
9202             delete El.cache[this.dom.id];
9203         },
9204
9205         /**
9206          * Sets up event handlers to add and remove a css class when the mouse is over this element
9207          * @param {String} className
9208          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9209          * mouseout events for children elements
9210          * @return {Roo.Element} this
9211          */
9212         addClassOnOver : function(className, preventFlicker){
9213             this.on("mouseover", function(){
9214                 Roo.fly(this, '_internal').addClass(className);
9215             }, this.dom);
9216             var removeFn = function(e){
9217                 if(preventFlicker !== true || !e.within(this, true)){
9218                     Roo.fly(this, '_internal').removeClass(className);
9219                 }
9220             };
9221             this.on("mouseout", removeFn, this.dom);
9222             return this;
9223         },
9224
9225         /**
9226          * Sets up event handlers to add and remove a css class when this element has the focus
9227          * @param {String} className
9228          * @return {Roo.Element} this
9229          */
9230         addClassOnFocus : function(className){
9231             this.on("focus", function(){
9232                 Roo.fly(this, '_internal').addClass(className);
9233             }, this.dom);
9234             this.on("blur", function(){
9235                 Roo.fly(this, '_internal').removeClass(className);
9236             }, this.dom);
9237             return this;
9238         },
9239         /**
9240          * 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)
9241          * @param {String} className
9242          * @return {Roo.Element} this
9243          */
9244         addClassOnClick : function(className){
9245             var dom = this.dom;
9246             this.on("mousedown", function(){
9247                 Roo.fly(dom, '_internal').addClass(className);
9248                 var d = Roo.get(document);
9249                 var fn = function(){
9250                     Roo.fly(dom, '_internal').removeClass(className);
9251                     d.removeListener("mouseup", fn);
9252                 };
9253                 d.on("mouseup", fn);
9254             });
9255             return this;
9256         },
9257
9258         /**
9259          * Stops the specified event from bubbling and optionally prevents the default action
9260          * @param {String} eventName
9261          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9262          * @return {Roo.Element} this
9263          */
9264         swallowEvent : function(eventName, preventDefault){
9265             var fn = function(e){
9266                 e.stopPropagation();
9267                 if(preventDefault){
9268                     e.preventDefault();
9269                 }
9270             };
9271             if(eventName instanceof Array){
9272                 for(var i = 0, len = eventName.length; i < len; i++){
9273                      this.on(eventName[i], fn);
9274                 }
9275                 return this;
9276             }
9277             this.on(eventName, fn);
9278             return this;
9279         },
9280
9281         /**
9282          * @private
9283          */
9284       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9285
9286         /**
9287          * Sizes this element to its parent element's dimensions performing
9288          * neccessary box adjustments.
9289          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9290          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9291          * @return {Roo.Element} this
9292          */
9293         fitToParent : function(monitorResize, targetParent) {
9294           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9295           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9296           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9297             return;
9298           }
9299           var p = Roo.get(targetParent || this.dom.parentNode);
9300           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9301           if (monitorResize === true) {
9302             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9303             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9304           }
9305           return this;
9306         },
9307
9308         /**
9309          * Gets the next sibling, skipping text nodes
9310          * @return {HTMLElement} The next sibling or null
9311          */
9312         getNextSibling : function(){
9313             var n = this.dom.nextSibling;
9314             while(n && n.nodeType != 1){
9315                 n = n.nextSibling;
9316             }
9317             return n;
9318         },
9319
9320         /**
9321          * Gets the previous sibling, skipping text nodes
9322          * @return {HTMLElement} The previous sibling or null
9323          */
9324         getPrevSibling : function(){
9325             var n = this.dom.previousSibling;
9326             while(n && n.nodeType != 1){
9327                 n = n.previousSibling;
9328             }
9329             return n;
9330         },
9331
9332
9333         /**
9334          * Appends the passed element(s) to this element
9335          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9336          * @return {Roo.Element} this
9337          */
9338         appendChild: function(el){
9339             el = Roo.get(el);
9340             el.appendTo(this);
9341             return this;
9342         },
9343
9344         /**
9345          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9346          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9347          * automatically generated with the specified attributes.
9348          * @param {HTMLElement} insertBefore (optional) a child element of this element
9349          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9350          * @return {Roo.Element} The new child element
9351          */
9352         createChild: function(config, insertBefore, returnDom){
9353             config = config || {tag:'div'};
9354             if(insertBefore){
9355                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9356             }
9357             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9358         },
9359
9360         /**
9361          * Appends this element to the passed element
9362          * @param {String/HTMLElement/Element} el The new parent element
9363          * @return {Roo.Element} this
9364          */
9365         appendTo: function(el){
9366             el = Roo.getDom(el);
9367             el.appendChild(this.dom);
9368             return this;
9369         },
9370
9371         /**
9372          * Inserts this element before the passed element in the DOM
9373          * @param {String/HTMLElement/Element} el The element to insert before
9374          * @return {Roo.Element} this
9375          */
9376         insertBefore: function(el){
9377             el = Roo.getDom(el);
9378             el.parentNode.insertBefore(this.dom, el);
9379             return this;
9380         },
9381
9382         /**
9383          * Inserts this element after the passed element in the DOM
9384          * @param {String/HTMLElement/Element} el The element to insert after
9385          * @return {Roo.Element} this
9386          */
9387         insertAfter: function(el){
9388             el = Roo.getDom(el);
9389             el.parentNode.insertBefore(this.dom, el.nextSibling);
9390             return this;
9391         },
9392
9393         /**
9394          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9395          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9396          * @return {Roo.Element} The new child
9397          */
9398         insertFirst: function(el, returnDom){
9399             el = el || {};
9400             if(typeof el == 'object' && !el.nodeType){ // dh config
9401                 return this.createChild(el, this.dom.firstChild, returnDom);
9402             }else{
9403                 el = Roo.getDom(el);
9404                 this.dom.insertBefore(el, this.dom.firstChild);
9405                 return !returnDom ? Roo.get(el) : el;
9406             }
9407         },
9408
9409         /**
9410          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9411          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9412          * @param {String} where (optional) 'before' or 'after' defaults to before
9413          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9414          * @return {Roo.Element} the inserted Element
9415          */
9416         insertSibling: function(el, where, returnDom){
9417             where = where ? where.toLowerCase() : 'before';
9418             el = el || {};
9419             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9420
9421             if(typeof el == 'object' && !el.nodeType){ // dh config
9422                 if(where == 'after' && !this.dom.nextSibling){
9423                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9424                 }else{
9425                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9426                 }
9427
9428             }else{
9429                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9430                             where == 'before' ? this.dom : this.dom.nextSibling);
9431                 if(!returnDom){
9432                     rt = Roo.get(rt);
9433                 }
9434             }
9435             return rt;
9436         },
9437
9438         /**
9439          * Creates and wraps this element with another element
9440          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9441          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9442          * @return {HTMLElement/Element} The newly created wrapper element
9443          */
9444         wrap: function(config, returnDom){
9445             if(!config){
9446                 config = {tag: "div"};
9447             }
9448             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9449             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9450             return newEl;
9451         },
9452
9453         /**
9454          * Replaces the passed element with this element
9455          * @param {String/HTMLElement/Element} el The element to replace
9456          * @return {Roo.Element} this
9457          */
9458         replace: function(el){
9459             el = Roo.get(el);
9460             this.insertBefore(el);
9461             el.remove();
9462             return this;
9463         },
9464
9465         /**
9466          * Inserts an html fragment into this element
9467          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9468          * @param {String} html The HTML fragment
9469          * @param {Boolean} returnEl True to return an Roo.Element
9470          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9471          */
9472         insertHtml : function(where, html, returnEl){
9473             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9474             return returnEl ? Roo.get(el) : el;
9475         },
9476
9477         /**
9478          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9479          * @param {Object} o The object with the attributes
9480          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9481          * @return {Roo.Element} this
9482          */
9483         set : function(o, useSet){
9484             var el = this.dom;
9485             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9486             for(var attr in o){
9487                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9488                 if(attr=="cls"){
9489                     el.className = o["cls"];
9490                 }else{
9491                     if(useSet) {
9492                         el.setAttribute(attr, o[attr]);
9493                     } else {
9494                         el[attr] = o[attr];
9495                     }
9496                 }
9497             }
9498             if(o.style){
9499                 Roo.DomHelper.applyStyles(el, o.style);
9500             }
9501             return this;
9502         },
9503
9504         /**
9505          * Convenience method for constructing a KeyMap
9506          * @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:
9507          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9508          * @param {Function} fn The function to call
9509          * @param {Object} scope (optional) The scope of the function
9510          * @return {Roo.KeyMap} The KeyMap created
9511          */
9512         addKeyListener : function(key, fn, scope){
9513             var config;
9514             if(typeof key != "object" || key instanceof Array){
9515                 config = {
9516                     key: key,
9517                     fn: fn,
9518                     scope: scope
9519                 };
9520             }else{
9521                 config = {
9522                     key : key.key,
9523                     shift : key.shift,
9524                     ctrl : key.ctrl,
9525                     alt : key.alt,
9526                     fn: fn,
9527                     scope: scope
9528                 };
9529             }
9530             return new Roo.KeyMap(this, config);
9531         },
9532
9533         /**
9534          * Creates a KeyMap for this element
9535          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9536          * @return {Roo.KeyMap} The KeyMap created
9537          */
9538         addKeyMap : function(config){
9539             return new Roo.KeyMap(this, config);
9540         },
9541
9542         /**
9543          * Returns true if this element is scrollable.
9544          * @return {Boolean}
9545          */
9546          isScrollable : function(){
9547             var dom = this.dom;
9548             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9549         },
9550
9551         /**
9552          * 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().
9553          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9554          * @param {Number} value The new scroll value
9555          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9556          * @return {Element} this
9557          */
9558
9559         scrollTo : function(side, value, animate){
9560             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9561             if(!animate || !A){
9562                 this.dom[prop] = value;
9563             }else{
9564                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9565                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9566             }
9567             return this;
9568         },
9569
9570         /**
9571          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9572          * within this element's scrollable range.
9573          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9574          * @param {Number} distance How far to scroll the element in pixels
9575          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9576          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9577          * was scrolled as far as it could go.
9578          */
9579          scroll : function(direction, distance, animate){
9580              if(!this.isScrollable()){
9581                  return;
9582              }
9583              var el = this.dom;
9584              var l = el.scrollLeft, t = el.scrollTop;
9585              var w = el.scrollWidth, h = el.scrollHeight;
9586              var cw = el.clientWidth, ch = el.clientHeight;
9587              direction = direction.toLowerCase();
9588              var scrolled = false;
9589              var a = this.preanim(arguments, 2);
9590              switch(direction){
9591                  case "l":
9592                  case "left":
9593                      if(w - l > cw){
9594                          var v = Math.min(l + distance, w-cw);
9595                          this.scrollTo("left", v, a);
9596                          scrolled = true;
9597                      }
9598                      break;
9599                 case "r":
9600                 case "right":
9601                      if(l > 0){
9602                          var v = Math.max(l - distance, 0);
9603                          this.scrollTo("left", v, a);
9604                          scrolled = true;
9605                      }
9606                      break;
9607                 case "t":
9608                 case "top":
9609                 case "up":
9610                      if(t > 0){
9611                          var v = Math.max(t - distance, 0);
9612                          this.scrollTo("top", v, a);
9613                          scrolled = true;
9614                      }
9615                      break;
9616                 case "b":
9617                 case "bottom":
9618                 case "down":
9619                      if(h - t > ch){
9620                          var v = Math.min(t + distance, h-ch);
9621                          this.scrollTo("top", v, a);
9622                          scrolled = true;
9623                      }
9624                      break;
9625              }
9626              return scrolled;
9627         },
9628
9629         /**
9630          * Translates the passed page coordinates into left/top css values for this element
9631          * @param {Number/Array} x The page x or an array containing [x, y]
9632          * @param {Number} y The page y
9633          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9634          */
9635         translatePoints : function(x, y){
9636             if(typeof x == 'object' || x instanceof Array){
9637                 y = x[1]; x = x[0];
9638             }
9639             var p = this.getStyle('position');
9640             var o = this.getXY();
9641
9642             var l = parseInt(this.getStyle('left'), 10);
9643             var t = parseInt(this.getStyle('top'), 10);
9644
9645             if(isNaN(l)){
9646                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9647             }
9648             if(isNaN(t)){
9649                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9650             }
9651
9652             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9653         },
9654
9655         /**
9656          * Returns the current scroll position of the element.
9657          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9658          */
9659         getScroll : function(){
9660             var d = this.dom, doc = document;
9661             if(d == doc || d == doc.body){
9662                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9663                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9664                 return {left: l, top: t};
9665             }else{
9666                 return {left: d.scrollLeft, top: d.scrollTop};
9667             }
9668         },
9669
9670         /**
9671          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9672          * are convert to standard 6 digit hex color.
9673          * @param {String} attr The css attribute
9674          * @param {String} defaultValue The default value to use when a valid color isn't found
9675          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9676          * YUI color anims.
9677          */
9678         getColor : function(attr, defaultValue, prefix){
9679             var v = this.getStyle(attr);
9680             if(!v || v == "transparent" || v == "inherit") {
9681                 return defaultValue;
9682             }
9683             var color = typeof prefix == "undefined" ? "#" : prefix;
9684             if(v.substr(0, 4) == "rgb("){
9685                 var rvs = v.slice(4, v.length -1).split(",");
9686                 for(var i = 0; i < 3; i++){
9687                     var h = parseInt(rvs[i]).toString(16);
9688                     if(h < 16){
9689                         h = "0" + h;
9690                     }
9691                     color += h;
9692                 }
9693             } else {
9694                 if(v.substr(0, 1) == "#"){
9695                     if(v.length == 4) {
9696                         for(var i = 1; i < 4; i++){
9697                             var c = v.charAt(i);
9698                             color +=  c + c;
9699                         }
9700                     }else if(v.length == 7){
9701                         color += v.substr(1);
9702                     }
9703                 }
9704             }
9705             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9706         },
9707
9708         /**
9709          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9710          * gradient background, rounded corners and a 4-way shadow.
9711          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9712          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9713          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9714          * @return {Roo.Element} this
9715          */
9716         boxWrap : function(cls){
9717             cls = cls || 'x-box';
9718             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9719             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9720             return el;
9721         },
9722
9723         /**
9724          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9725          * @param {String} namespace The namespace in which to look for the attribute
9726          * @param {String} name The attribute name
9727          * @return {String} The attribute value
9728          */
9729         getAttributeNS : Roo.isIE ? function(ns, name){
9730             var d = this.dom;
9731             var type = typeof d[ns+":"+name];
9732             if(type != 'undefined' && type != 'unknown'){
9733                 return d[ns+":"+name];
9734             }
9735             return d[name];
9736         } : function(ns, name){
9737             var d = this.dom;
9738             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9739         },
9740         
9741         
9742         /**
9743          * Sets or Returns the value the dom attribute value
9744          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9745          * @param {String} value (optional) The value to set the attribute to
9746          * @return {String} The attribute value
9747          */
9748         attr : function(name){
9749             if (arguments.length > 1) {
9750                 this.dom.setAttribute(name, arguments[1]);
9751                 return arguments[1];
9752             }
9753             if (typeof(name) == 'object') {
9754                 for(var i in name) {
9755                     this.attr(i, name[i]);
9756                 }
9757                 return name;
9758             }
9759             
9760             
9761             if (!this.dom.hasAttribute(name)) {
9762                 return undefined;
9763             }
9764             return this.dom.getAttribute(name);
9765         }
9766         
9767         
9768         
9769     };
9770
9771     var ep = El.prototype;
9772
9773     /**
9774      * Appends an event handler (Shorthand for addListener)
9775      * @param {String}   eventName     The type of event to append
9776      * @param {Function} fn        The method the event invokes
9777      * @param {Object} scope       (optional) The scope (this object) of the fn
9778      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9779      * @method
9780      */
9781     ep.on = ep.addListener;
9782         // backwards compat
9783     ep.mon = ep.addListener;
9784
9785     /**
9786      * Removes an event handler from this element (shorthand for removeListener)
9787      * @param {String} eventName the type of event to remove
9788      * @param {Function} fn the method the event invokes
9789      * @return {Roo.Element} this
9790      * @method
9791      */
9792     ep.un = ep.removeListener;
9793
9794     /**
9795      * true to automatically adjust width and height settings for box-model issues (default to true)
9796      */
9797     ep.autoBoxAdjust = true;
9798
9799     // private
9800     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9801
9802     // private
9803     El.addUnits = function(v, defaultUnit){
9804         if(v === "" || v == "auto"){
9805             return v;
9806         }
9807         if(v === undefined){
9808             return '';
9809         }
9810         if(typeof v == "number" || !El.unitPattern.test(v)){
9811             return v + (defaultUnit || 'px');
9812         }
9813         return v;
9814     };
9815
9816     // special markup used throughout Roo when box wrapping elements
9817     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>';
9818     /**
9819      * Visibility mode constant - Use visibility to hide element
9820      * @static
9821      * @type Number
9822      */
9823     El.VISIBILITY = 1;
9824     /**
9825      * Visibility mode constant - Use display to hide element
9826      * @static
9827      * @type Number
9828      */
9829     El.DISPLAY = 2;
9830
9831     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9832     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9833     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9834
9835
9836
9837     /**
9838      * @private
9839      */
9840     El.cache = {};
9841
9842     var docEl;
9843
9844     /**
9845      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9846      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9847      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9848      * @return {Element} The Element object
9849      * @static
9850      */
9851     El.get = function(el){
9852         var ex, elm, id;
9853         if(!el){ return null; }
9854         if(typeof el == "string"){ // element id
9855             if(!(elm = document.getElementById(el))){
9856                 return null;
9857             }
9858             if(ex = El.cache[el]){
9859                 ex.dom = elm;
9860             }else{
9861                 ex = El.cache[el] = new El(elm);
9862             }
9863             return ex;
9864         }else if(el.tagName){ // dom element
9865             if(!(id = el.id)){
9866                 id = Roo.id(el);
9867             }
9868             if(ex = El.cache[id]){
9869                 ex.dom = el;
9870             }else{
9871                 ex = El.cache[id] = new El(el);
9872             }
9873             return ex;
9874         }else if(el instanceof El){
9875             if(el != docEl){
9876                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9877                                                               // catch case where it hasn't been appended
9878                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9879             }
9880             return el;
9881         }else if(el.isComposite){
9882             return el;
9883         }else if(el instanceof Array){
9884             return El.select(el);
9885         }else if(el == document){
9886             // create a bogus element object representing the document object
9887             if(!docEl){
9888                 var f = function(){};
9889                 f.prototype = El.prototype;
9890                 docEl = new f();
9891                 docEl.dom = document;
9892             }
9893             return docEl;
9894         }
9895         return null;
9896     };
9897
9898     // private
9899     El.uncache = function(el){
9900         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9901             if(a[i]){
9902                 delete El.cache[a[i].id || a[i]];
9903             }
9904         }
9905     };
9906
9907     // private
9908     // Garbage collection - uncache elements/purge listeners on orphaned elements
9909     // so we don't hold a reference and cause the browser to retain them
9910     El.garbageCollect = function(){
9911         if(!Roo.enableGarbageCollector){
9912             clearInterval(El.collectorThread);
9913             return;
9914         }
9915         for(var eid in El.cache){
9916             var el = El.cache[eid], d = el.dom;
9917             // -------------------------------------------------------
9918             // Determining what is garbage:
9919             // -------------------------------------------------------
9920             // !d
9921             // dom node is null, definitely garbage
9922             // -------------------------------------------------------
9923             // !d.parentNode
9924             // no parentNode == direct orphan, definitely garbage
9925             // -------------------------------------------------------
9926             // !d.offsetParent && !document.getElementById(eid)
9927             // display none elements have no offsetParent so we will
9928             // also try to look it up by it's id. However, check
9929             // offsetParent first so we don't do unneeded lookups.
9930             // This enables collection of elements that are not orphans
9931             // directly, but somewhere up the line they have an orphan
9932             // parent.
9933             // -------------------------------------------------------
9934             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9935                 delete El.cache[eid];
9936                 if(d && Roo.enableListenerCollection){
9937                     E.purgeElement(d);
9938                 }
9939             }
9940         }
9941     }
9942     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9943
9944
9945     // dom is optional
9946     El.Flyweight = function(dom){
9947         this.dom = dom;
9948     };
9949     El.Flyweight.prototype = El.prototype;
9950
9951     El._flyweights = {};
9952     /**
9953      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9954      * the dom node can be overwritten by other code.
9955      * @param {String/HTMLElement} el The dom node or id
9956      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9957      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9958      * @static
9959      * @return {Element} The shared Element object
9960      */
9961     El.fly = function(el, named){
9962         named = named || '_global';
9963         el = Roo.getDom(el);
9964         if(!el){
9965             return null;
9966         }
9967         if(!El._flyweights[named]){
9968             El._flyweights[named] = new El.Flyweight();
9969         }
9970         El._flyweights[named].dom = el;
9971         return El._flyweights[named];
9972     };
9973
9974     /**
9975      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9976      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9977      * Shorthand of {@link Roo.Element#get}
9978      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9979      * @return {Element} The Element object
9980      * @member Roo
9981      * @method get
9982      */
9983     Roo.get = El.get;
9984     /**
9985      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9986      * the dom node can be overwritten by other code.
9987      * Shorthand of {@link Roo.Element#fly}
9988      * @param {String/HTMLElement} el The dom node or id
9989      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9990      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9991      * @static
9992      * @return {Element} The shared Element object
9993      * @member Roo
9994      * @method fly
9995      */
9996     Roo.fly = El.fly;
9997
9998     // speedy lookup for elements never to box adjust
9999     var noBoxAdjust = Roo.isStrict ? {
10000         select:1
10001     } : {
10002         input:1, select:1, textarea:1
10003     };
10004     if(Roo.isIE || Roo.isGecko){
10005         noBoxAdjust['button'] = 1;
10006     }
10007
10008
10009     Roo.EventManager.on(window, 'unload', function(){
10010         delete El.cache;
10011         delete El._flyweights;
10012     });
10013 })();
10014
10015
10016
10017
10018 if(Roo.DomQuery){
10019     Roo.Element.selectorFunction = Roo.DomQuery.select;
10020 }
10021
10022 Roo.Element.select = function(selector, unique, root){
10023     var els;
10024     if(typeof selector == "string"){
10025         els = Roo.Element.selectorFunction(selector, root);
10026     }else if(selector.length !== undefined){
10027         els = selector;
10028     }else{
10029         throw "Invalid selector";
10030     }
10031     if(unique === true){
10032         return new Roo.CompositeElement(els);
10033     }else{
10034         return new Roo.CompositeElementLite(els);
10035     }
10036 };
10037 /**
10038  * Selects elements based on the passed CSS selector to enable working on them as 1.
10039  * @param {String/Array} selector The CSS selector or an array of elements
10040  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10041  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10042  * @return {CompositeElementLite/CompositeElement}
10043  * @member Roo
10044  * @method select
10045  */
10046 Roo.select = Roo.Element.select;
10047
10048
10049
10050
10051
10052
10053
10054
10055
10056
10057
10058
10059
10060
10061 /*
10062  * Based on:
10063  * Ext JS Library 1.1.1
10064  * Copyright(c) 2006-2007, Ext JS, LLC.
10065  *
10066  * Originally Released Under LGPL - original licence link has changed is not relivant.
10067  *
10068  * Fork - LGPL
10069  * <script type="text/javascript">
10070  */
10071
10072
10073
10074 //Notifies Element that fx methods are available
10075 Roo.enableFx = true;
10076
10077 /**
10078  * @class Roo.Fx
10079  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10080  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10081  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10082  * Element effects to work.</p><br/>
10083  *
10084  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10085  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10086  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10087  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10088  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10089  * expected results and should be done with care.</p><br/>
10090  *
10091  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10092  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10093 <pre>
10094 Value  Description
10095 -----  -----------------------------
10096 tl     The top left corner
10097 t      The center of the top edge
10098 tr     The top right corner
10099 l      The center of the left edge
10100 r      The center of the right edge
10101 bl     The bottom left corner
10102 b      The center of the bottom edge
10103 br     The bottom right corner
10104 </pre>
10105  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10106  * below are common options that can be passed to any Fx method.</b>
10107  * @cfg {Function} callback A function called when the effect is finished
10108  * @cfg {Object} scope The scope of the effect function
10109  * @cfg {String} easing A valid Easing value for the effect
10110  * @cfg {String} afterCls A css class to apply after the effect
10111  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10112  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10113  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10114  * effects that end with the element being visually hidden, ignored otherwise)
10115  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10116  * a function which returns such a specification that will be applied to the Element after the effect finishes
10117  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10118  * @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
10119  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10120  */
10121 Roo.Fx = {
10122         /**
10123          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10124          * origin for the slide effect.  This function automatically handles wrapping the element with
10125          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10126          * Usage:
10127          *<pre><code>
10128 // default: slide the element in from the top
10129 el.slideIn();
10130
10131 // custom: slide the element in from the right with a 2-second duration
10132 el.slideIn('r', { duration: 2 });
10133
10134 // common config options shown with default values
10135 el.slideIn('t', {
10136     easing: 'easeOut',
10137     duration: .5
10138 });
10139 </code></pre>
10140          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10141          * @param {Object} options (optional) Object literal with any of the Fx config options
10142          * @return {Roo.Element} The Element
10143          */
10144     slideIn : function(anchor, o){
10145         var el = this.getFxEl();
10146         o = o || {};
10147
10148         el.queueFx(o, function(){
10149
10150             anchor = anchor || "t";
10151
10152             // fix display to visibility
10153             this.fixDisplay();
10154
10155             // restore values after effect
10156             var r = this.getFxRestore();
10157             var b = this.getBox();
10158             // fixed size for slide
10159             this.setSize(b);
10160
10161             // wrap if needed
10162             var wrap = this.fxWrap(r.pos, o, "hidden");
10163
10164             var st = this.dom.style;
10165             st.visibility = "visible";
10166             st.position = "absolute";
10167
10168             // clear out temp styles after slide and unwrap
10169             var after = function(){
10170                 el.fxUnwrap(wrap, r.pos, o);
10171                 st.width = r.width;
10172                 st.height = r.height;
10173                 el.afterFx(o);
10174             };
10175             // time to calc the positions
10176             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10177
10178             switch(anchor.toLowerCase()){
10179                 case "t":
10180                     wrap.setSize(b.width, 0);
10181                     st.left = st.bottom = "0";
10182                     a = {height: bh};
10183                 break;
10184                 case "l":
10185                     wrap.setSize(0, b.height);
10186                     st.right = st.top = "0";
10187                     a = {width: bw};
10188                 break;
10189                 case "r":
10190                     wrap.setSize(0, b.height);
10191                     wrap.setX(b.right);
10192                     st.left = st.top = "0";
10193                     a = {width: bw, points: pt};
10194                 break;
10195                 case "b":
10196                     wrap.setSize(b.width, 0);
10197                     wrap.setY(b.bottom);
10198                     st.left = st.top = "0";
10199                     a = {height: bh, points: pt};
10200                 break;
10201                 case "tl":
10202                     wrap.setSize(0, 0);
10203                     st.right = st.bottom = "0";
10204                     a = {width: bw, height: bh};
10205                 break;
10206                 case "bl":
10207                     wrap.setSize(0, 0);
10208                     wrap.setY(b.y+b.height);
10209                     st.right = st.top = "0";
10210                     a = {width: bw, height: bh, points: pt};
10211                 break;
10212                 case "br":
10213                     wrap.setSize(0, 0);
10214                     wrap.setXY([b.right, b.bottom]);
10215                     st.left = st.top = "0";
10216                     a = {width: bw, height: bh, points: pt};
10217                 break;
10218                 case "tr":
10219                     wrap.setSize(0, 0);
10220                     wrap.setX(b.x+b.width);
10221                     st.left = st.bottom = "0";
10222                     a = {width: bw, height: bh, points: pt};
10223                 break;
10224             }
10225             this.dom.style.visibility = "visible";
10226             wrap.show();
10227
10228             arguments.callee.anim = wrap.fxanim(a,
10229                 o,
10230                 'motion',
10231                 .5,
10232                 'easeOut', after);
10233         });
10234         return this;
10235     },
10236     
10237         /**
10238          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10239          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10240          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10241          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10242          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10243          * Usage:
10244          *<pre><code>
10245 // default: slide the element out to the top
10246 el.slideOut();
10247
10248 // custom: slide the element out to the right with a 2-second duration
10249 el.slideOut('r', { duration: 2 });
10250
10251 // common config options shown with default values
10252 el.slideOut('t', {
10253     easing: 'easeOut',
10254     duration: .5,
10255     remove: false,
10256     useDisplay: false
10257 });
10258 </code></pre>
10259          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10260          * @param {Object} options (optional) Object literal with any of the Fx config options
10261          * @return {Roo.Element} The Element
10262          */
10263     slideOut : function(anchor, o){
10264         var el = this.getFxEl();
10265         o = o || {};
10266
10267         el.queueFx(o, function(){
10268
10269             anchor = anchor || "t";
10270
10271             // restore values after effect
10272             var r = this.getFxRestore();
10273             
10274             var b = this.getBox();
10275             // fixed size for slide
10276             this.setSize(b);
10277
10278             // wrap if needed
10279             var wrap = this.fxWrap(r.pos, o, "visible");
10280
10281             var st = this.dom.style;
10282             st.visibility = "visible";
10283             st.position = "absolute";
10284
10285             wrap.setSize(b);
10286
10287             var after = function(){
10288                 if(o.useDisplay){
10289                     el.setDisplayed(false);
10290                 }else{
10291                     el.hide();
10292                 }
10293
10294                 el.fxUnwrap(wrap, r.pos, o);
10295
10296                 st.width = r.width;
10297                 st.height = r.height;
10298
10299                 el.afterFx(o);
10300             };
10301
10302             var a, zero = {to: 0};
10303             switch(anchor.toLowerCase()){
10304                 case "t":
10305                     st.left = st.bottom = "0";
10306                     a = {height: zero};
10307                 break;
10308                 case "l":
10309                     st.right = st.top = "0";
10310                     a = {width: zero};
10311                 break;
10312                 case "r":
10313                     st.left = st.top = "0";
10314                     a = {width: zero, points: {to:[b.right, b.y]}};
10315                 break;
10316                 case "b":
10317                     st.left = st.top = "0";
10318                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10319                 break;
10320                 case "tl":
10321                     st.right = st.bottom = "0";
10322                     a = {width: zero, height: zero};
10323                 break;
10324                 case "bl":
10325                     st.right = st.top = "0";
10326                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10327                 break;
10328                 case "br":
10329                     st.left = st.top = "0";
10330                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10331                 break;
10332                 case "tr":
10333                     st.left = st.bottom = "0";
10334                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10335                 break;
10336             }
10337
10338             arguments.callee.anim = wrap.fxanim(a,
10339                 o,
10340                 'motion',
10341                 .5,
10342                 "easeOut", after);
10343         });
10344         return this;
10345     },
10346
10347         /**
10348          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10349          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10350          * The element must be removed from the DOM using the 'remove' config option if desired.
10351          * Usage:
10352          *<pre><code>
10353 // default
10354 el.puff();
10355
10356 // common config options shown with default values
10357 el.puff({
10358     easing: 'easeOut',
10359     duration: .5,
10360     remove: false,
10361     useDisplay: false
10362 });
10363 </code></pre>
10364          * @param {Object} options (optional) Object literal with any of the Fx config options
10365          * @return {Roo.Element} The Element
10366          */
10367     puff : function(o){
10368         var el = this.getFxEl();
10369         o = o || {};
10370
10371         el.queueFx(o, function(){
10372             this.clearOpacity();
10373             this.show();
10374
10375             // restore values after effect
10376             var r = this.getFxRestore();
10377             var st = this.dom.style;
10378
10379             var after = function(){
10380                 if(o.useDisplay){
10381                     el.setDisplayed(false);
10382                 }else{
10383                     el.hide();
10384                 }
10385
10386                 el.clearOpacity();
10387
10388                 el.setPositioning(r.pos);
10389                 st.width = r.width;
10390                 st.height = r.height;
10391                 st.fontSize = '';
10392                 el.afterFx(o);
10393             };
10394
10395             var width = this.getWidth();
10396             var height = this.getHeight();
10397
10398             arguments.callee.anim = this.fxanim({
10399                     width : {to: this.adjustWidth(width * 2)},
10400                     height : {to: this.adjustHeight(height * 2)},
10401                     points : {by: [-(width * .5), -(height * .5)]},
10402                     opacity : {to: 0},
10403                     fontSize: {to:200, unit: "%"}
10404                 },
10405                 o,
10406                 'motion',
10407                 .5,
10408                 "easeOut", after);
10409         });
10410         return this;
10411     },
10412
10413         /**
10414          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10415          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10416          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10417          * Usage:
10418          *<pre><code>
10419 // default
10420 el.switchOff();
10421
10422 // all config options shown with default values
10423 el.switchOff({
10424     easing: 'easeIn',
10425     duration: .3,
10426     remove: false,
10427     useDisplay: false
10428 });
10429 </code></pre>
10430          * @param {Object} options (optional) Object literal with any of the Fx config options
10431          * @return {Roo.Element} The Element
10432          */
10433     switchOff : function(o){
10434         var el = this.getFxEl();
10435         o = o || {};
10436
10437         el.queueFx(o, function(){
10438             this.clearOpacity();
10439             this.clip();
10440
10441             // restore values after effect
10442             var r = this.getFxRestore();
10443             var st = this.dom.style;
10444
10445             var after = function(){
10446                 if(o.useDisplay){
10447                     el.setDisplayed(false);
10448                 }else{
10449                     el.hide();
10450                 }
10451
10452                 el.clearOpacity();
10453                 el.setPositioning(r.pos);
10454                 st.width = r.width;
10455                 st.height = r.height;
10456
10457                 el.afterFx(o);
10458             };
10459
10460             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10461                 this.clearOpacity();
10462                 (function(){
10463                     this.fxanim({
10464                         height:{to:1},
10465                         points:{by:[0, this.getHeight() * .5]}
10466                     }, o, 'motion', 0.3, 'easeIn', after);
10467                 }).defer(100, this);
10468             });
10469         });
10470         return this;
10471     },
10472
10473     /**
10474      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10475      * changed using the "attr" config option) and then fading back to the original color. If no original
10476      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10477      * Usage:
10478 <pre><code>
10479 // default: highlight background to yellow
10480 el.highlight();
10481
10482 // custom: highlight foreground text to blue for 2 seconds
10483 el.highlight("0000ff", { attr: 'color', duration: 2 });
10484
10485 // common config options shown with default values
10486 el.highlight("ffff9c", {
10487     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10488     endColor: (current color) or "ffffff",
10489     easing: 'easeIn',
10490     duration: 1
10491 });
10492 </code></pre>
10493      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10494      * @param {Object} options (optional) Object literal with any of the Fx config options
10495      * @return {Roo.Element} The Element
10496      */ 
10497     highlight : function(color, o){
10498         var el = this.getFxEl();
10499         o = o || {};
10500
10501         el.queueFx(o, function(){
10502             color = color || "ffff9c";
10503             attr = o.attr || "backgroundColor";
10504
10505             this.clearOpacity();
10506             this.show();
10507
10508             var origColor = this.getColor(attr);
10509             var restoreColor = this.dom.style[attr];
10510             endColor = (o.endColor || origColor) || "ffffff";
10511
10512             var after = function(){
10513                 el.dom.style[attr] = restoreColor;
10514                 el.afterFx(o);
10515             };
10516
10517             var a = {};
10518             a[attr] = {from: color, to: endColor};
10519             arguments.callee.anim = this.fxanim(a,
10520                 o,
10521                 'color',
10522                 1,
10523                 'easeIn', after);
10524         });
10525         return this;
10526     },
10527
10528    /**
10529     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10530     * Usage:
10531 <pre><code>
10532 // default: a single light blue ripple
10533 el.frame();
10534
10535 // custom: 3 red ripples lasting 3 seconds total
10536 el.frame("ff0000", 3, { duration: 3 });
10537
10538 // common config options shown with default values
10539 el.frame("C3DAF9", 1, {
10540     duration: 1 //duration of entire animation (not each individual ripple)
10541     // Note: Easing is not configurable and will be ignored if included
10542 });
10543 </code></pre>
10544     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10545     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10546     * @param {Object} options (optional) Object literal with any of the Fx config options
10547     * @return {Roo.Element} The Element
10548     */
10549     frame : function(color, count, o){
10550         var el = this.getFxEl();
10551         o = o || {};
10552
10553         el.queueFx(o, function(){
10554             color = color || "#C3DAF9";
10555             if(color.length == 6){
10556                 color = "#" + color;
10557             }
10558             count = count || 1;
10559             duration = o.duration || 1;
10560             this.show();
10561
10562             var b = this.getBox();
10563             var animFn = function(){
10564                 var proxy = this.createProxy({
10565
10566                      style:{
10567                         visbility:"hidden",
10568                         position:"absolute",
10569                         "z-index":"35000", // yee haw
10570                         border:"0px solid " + color
10571                      }
10572                   });
10573                 var scale = Roo.isBorderBox ? 2 : 1;
10574                 proxy.animate({
10575                     top:{from:b.y, to:b.y - 20},
10576                     left:{from:b.x, to:b.x - 20},
10577                     borderWidth:{from:0, to:10},
10578                     opacity:{from:1, to:0},
10579                     height:{from:b.height, to:(b.height + (20*scale))},
10580                     width:{from:b.width, to:(b.width + (20*scale))}
10581                 }, duration, function(){
10582                     proxy.remove();
10583                 });
10584                 if(--count > 0){
10585                      animFn.defer((duration/2)*1000, this);
10586                 }else{
10587                     el.afterFx(o);
10588                 }
10589             };
10590             animFn.call(this);
10591         });
10592         return this;
10593     },
10594
10595    /**
10596     * Creates a pause before any subsequent queued effects begin.  If there are
10597     * no effects queued after the pause it will have no effect.
10598     * Usage:
10599 <pre><code>
10600 el.pause(1);
10601 </code></pre>
10602     * @param {Number} seconds The length of time to pause (in seconds)
10603     * @return {Roo.Element} The Element
10604     */
10605     pause : function(seconds){
10606         var el = this.getFxEl();
10607         var o = {};
10608
10609         el.queueFx(o, function(){
10610             setTimeout(function(){
10611                 el.afterFx(o);
10612             }, seconds * 1000);
10613         });
10614         return this;
10615     },
10616
10617    /**
10618     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10619     * using the "endOpacity" config option.
10620     * Usage:
10621 <pre><code>
10622 // default: fade in from opacity 0 to 100%
10623 el.fadeIn();
10624
10625 // custom: fade in from opacity 0 to 75% over 2 seconds
10626 el.fadeIn({ endOpacity: .75, duration: 2});
10627
10628 // common config options shown with default values
10629 el.fadeIn({
10630     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10631     easing: 'easeOut',
10632     duration: .5
10633 });
10634 </code></pre>
10635     * @param {Object} options (optional) Object literal with any of the Fx config options
10636     * @return {Roo.Element} The Element
10637     */
10638     fadeIn : function(o){
10639         var el = this.getFxEl();
10640         o = o || {};
10641         el.queueFx(o, function(){
10642             this.setOpacity(0);
10643             this.fixDisplay();
10644             this.dom.style.visibility = 'visible';
10645             var to = o.endOpacity || 1;
10646             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10647                 o, null, .5, "easeOut", function(){
10648                 if(to == 1){
10649                     this.clearOpacity();
10650                 }
10651                 el.afterFx(o);
10652             });
10653         });
10654         return this;
10655     },
10656
10657    /**
10658     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10659     * using the "endOpacity" config option.
10660     * Usage:
10661 <pre><code>
10662 // default: fade out from the element's current opacity to 0
10663 el.fadeOut();
10664
10665 // custom: fade out from the element's current opacity to 25% over 2 seconds
10666 el.fadeOut({ endOpacity: .25, duration: 2});
10667
10668 // common config options shown with default values
10669 el.fadeOut({
10670     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10671     easing: 'easeOut',
10672     duration: .5
10673     remove: false,
10674     useDisplay: false
10675 });
10676 </code></pre>
10677     * @param {Object} options (optional) Object literal with any of the Fx config options
10678     * @return {Roo.Element} The Element
10679     */
10680     fadeOut : function(o){
10681         var el = this.getFxEl();
10682         o = o || {};
10683         el.queueFx(o, function(){
10684             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10685                 o, null, .5, "easeOut", function(){
10686                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10687                      this.dom.style.display = "none";
10688                 }else{
10689                      this.dom.style.visibility = "hidden";
10690                 }
10691                 this.clearOpacity();
10692                 el.afterFx(o);
10693             });
10694         });
10695         return this;
10696     },
10697
10698    /**
10699     * Animates the transition of an element's dimensions from a starting height/width
10700     * to an ending height/width.
10701     * Usage:
10702 <pre><code>
10703 // change height and width to 100x100 pixels
10704 el.scale(100, 100);
10705
10706 // common config options shown with default values.  The height and width will default to
10707 // the element's existing values if passed as null.
10708 el.scale(
10709     [element's width],
10710     [element's height], {
10711     easing: 'easeOut',
10712     duration: .35
10713 });
10714 </code></pre>
10715     * @param {Number} width  The new width (pass undefined to keep the original width)
10716     * @param {Number} height  The new height (pass undefined to keep the original height)
10717     * @param {Object} options (optional) Object literal with any of the Fx config options
10718     * @return {Roo.Element} The Element
10719     */
10720     scale : function(w, h, o){
10721         this.shift(Roo.apply({}, o, {
10722             width: w,
10723             height: h
10724         }));
10725         return this;
10726     },
10727
10728    /**
10729     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10730     * Any of these properties not specified in the config object will not be changed.  This effect 
10731     * requires that at least one new dimension, position or opacity setting must be passed in on
10732     * the config object in order for the function to have any effect.
10733     * Usage:
10734 <pre><code>
10735 // slide the element horizontally to x position 200 while changing the height and opacity
10736 el.shift({ x: 200, height: 50, opacity: .8 });
10737
10738 // common config options shown with default values.
10739 el.shift({
10740     width: [element's width],
10741     height: [element's height],
10742     x: [element's x position],
10743     y: [element's y position],
10744     opacity: [element's opacity],
10745     easing: 'easeOut',
10746     duration: .35
10747 });
10748 </code></pre>
10749     * @param {Object} options  Object literal with any of the Fx config options
10750     * @return {Roo.Element} The Element
10751     */
10752     shift : function(o){
10753         var el = this.getFxEl();
10754         o = o || {};
10755         el.queueFx(o, function(){
10756             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10757             if(w !== undefined){
10758                 a.width = {to: this.adjustWidth(w)};
10759             }
10760             if(h !== undefined){
10761                 a.height = {to: this.adjustHeight(h)};
10762             }
10763             if(x !== undefined || y !== undefined){
10764                 a.points = {to: [
10765                     x !== undefined ? x : this.getX(),
10766                     y !== undefined ? y : this.getY()
10767                 ]};
10768             }
10769             if(op !== undefined){
10770                 a.opacity = {to: op};
10771             }
10772             if(o.xy !== undefined){
10773                 a.points = {to: o.xy};
10774             }
10775             arguments.callee.anim = this.fxanim(a,
10776                 o, 'motion', .35, "easeOut", function(){
10777                 el.afterFx(o);
10778             });
10779         });
10780         return this;
10781     },
10782
10783         /**
10784          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10785          * ending point of the effect.
10786          * Usage:
10787          *<pre><code>
10788 // default: slide the element downward while fading out
10789 el.ghost();
10790
10791 // custom: slide the element out to the right with a 2-second duration
10792 el.ghost('r', { duration: 2 });
10793
10794 // common config options shown with default values
10795 el.ghost('b', {
10796     easing: 'easeOut',
10797     duration: .5
10798     remove: false,
10799     useDisplay: false
10800 });
10801 </code></pre>
10802          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10803          * @param {Object} options (optional) Object literal with any of the Fx config options
10804          * @return {Roo.Element} The Element
10805          */
10806     ghost : function(anchor, o){
10807         var el = this.getFxEl();
10808         o = o || {};
10809
10810         el.queueFx(o, function(){
10811             anchor = anchor || "b";
10812
10813             // restore values after effect
10814             var r = this.getFxRestore();
10815             var w = this.getWidth(),
10816                 h = this.getHeight();
10817
10818             var st = this.dom.style;
10819
10820             var after = function(){
10821                 if(o.useDisplay){
10822                     el.setDisplayed(false);
10823                 }else{
10824                     el.hide();
10825                 }
10826
10827                 el.clearOpacity();
10828                 el.setPositioning(r.pos);
10829                 st.width = r.width;
10830                 st.height = r.height;
10831
10832                 el.afterFx(o);
10833             };
10834
10835             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10836             switch(anchor.toLowerCase()){
10837                 case "t":
10838                     pt.by = [0, -h];
10839                 break;
10840                 case "l":
10841                     pt.by = [-w, 0];
10842                 break;
10843                 case "r":
10844                     pt.by = [w, 0];
10845                 break;
10846                 case "b":
10847                     pt.by = [0, h];
10848                 break;
10849                 case "tl":
10850                     pt.by = [-w, -h];
10851                 break;
10852                 case "bl":
10853                     pt.by = [-w, h];
10854                 break;
10855                 case "br":
10856                     pt.by = [w, h];
10857                 break;
10858                 case "tr":
10859                     pt.by = [w, -h];
10860                 break;
10861             }
10862
10863             arguments.callee.anim = this.fxanim(a,
10864                 o,
10865                 'motion',
10866                 .5,
10867                 "easeOut", after);
10868         });
10869         return this;
10870     },
10871
10872         /**
10873          * Ensures that all effects queued after syncFx is called on the element are
10874          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10875          * @return {Roo.Element} The Element
10876          */
10877     syncFx : function(){
10878         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10879             block : false,
10880             concurrent : true,
10881             stopFx : false
10882         });
10883         return this;
10884     },
10885
10886         /**
10887          * Ensures that all effects queued after sequenceFx is called on the element are
10888          * run in sequence.  This is the opposite of {@link #syncFx}.
10889          * @return {Roo.Element} The Element
10890          */
10891     sequenceFx : function(){
10892         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10893             block : false,
10894             concurrent : false,
10895             stopFx : false
10896         });
10897         return this;
10898     },
10899
10900         /* @private */
10901     nextFx : function(){
10902         var ef = this.fxQueue[0];
10903         if(ef){
10904             ef.call(this);
10905         }
10906     },
10907
10908         /**
10909          * Returns true if the element has any effects actively running or queued, else returns false.
10910          * @return {Boolean} True if element has active effects, else false
10911          */
10912     hasActiveFx : function(){
10913         return this.fxQueue && this.fxQueue[0];
10914     },
10915
10916         /**
10917          * Stops any running effects and clears the element's internal effects queue if it contains
10918          * any additional effects that haven't started yet.
10919          * @return {Roo.Element} The Element
10920          */
10921     stopFx : function(){
10922         if(this.hasActiveFx()){
10923             var cur = this.fxQueue[0];
10924             if(cur && cur.anim && cur.anim.isAnimated()){
10925                 this.fxQueue = [cur]; // clear out others
10926                 cur.anim.stop(true);
10927             }
10928         }
10929         return this;
10930     },
10931
10932         /* @private */
10933     beforeFx : function(o){
10934         if(this.hasActiveFx() && !o.concurrent){
10935            if(o.stopFx){
10936                this.stopFx();
10937                return true;
10938            }
10939            return false;
10940         }
10941         return true;
10942     },
10943
10944         /**
10945          * Returns true if the element is currently blocking so that no other effect can be queued
10946          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10947          * used to ensure that an effect initiated by a user action runs to completion prior to the
10948          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10949          * @return {Boolean} True if blocking, else false
10950          */
10951     hasFxBlock : function(){
10952         var q = this.fxQueue;
10953         return q && q[0] && q[0].block;
10954     },
10955
10956         /* @private */
10957     queueFx : function(o, fn){
10958         if(!this.fxQueue){
10959             this.fxQueue = [];
10960         }
10961         if(!this.hasFxBlock()){
10962             Roo.applyIf(o, this.fxDefaults);
10963             if(!o.concurrent){
10964                 var run = this.beforeFx(o);
10965                 fn.block = o.block;
10966                 this.fxQueue.push(fn);
10967                 if(run){
10968                     this.nextFx();
10969                 }
10970             }else{
10971                 fn.call(this);
10972             }
10973         }
10974         return this;
10975     },
10976
10977         /* @private */
10978     fxWrap : function(pos, o, vis){
10979         var wrap;
10980         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10981             var wrapXY;
10982             if(o.fixPosition){
10983                 wrapXY = this.getXY();
10984             }
10985             var div = document.createElement("div");
10986             div.style.visibility = vis;
10987             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10988             wrap.setPositioning(pos);
10989             if(wrap.getStyle("position") == "static"){
10990                 wrap.position("relative");
10991             }
10992             this.clearPositioning('auto');
10993             wrap.clip();
10994             wrap.dom.appendChild(this.dom);
10995             if(wrapXY){
10996                 wrap.setXY(wrapXY);
10997             }
10998         }
10999         return wrap;
11000     },
11001
11002         /* @private */
11003     fxUnwrap : function(wrap, pos, o){
11004         this.clearPositioning();
11005         this.setPositioning(pos);
11006         if(!o.wrap){
11007             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11008             wrap.remove();
11009         }
11010     },
11011
11012         /* @private */
11013     getFxRestore : function(){
11014         var st = this.dom.style;
11015         return {pos: this.getPositioning(), width: st.width, height : st.height};
11016     },
11017
11018         /* @private */
11019     afterFx : function(o){
11020         if(o.afterStyle){
11021             this.applyStyles(o.afterStyle);
11022         }
11023         if(o.afterCls){
11024             this.addClass(o.afterCls);
11025         }
11026         if(o.remove === true){
11027             this.remove();
11028         }
11029         Roo.callback(o.callback, o.scope, [this]);
11030         if(!o.concurrent){
11031             this.fxQueue.shift();
11032             this.nextFx();
11033         }
11034     },
11035
11036         /* @private */
11037     getFxEl : function(){ // support for composite element fx
11038         return Roo.get(this.dom);
11039     },
11040
11041         /* @private */
11042     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11043         animType = animType || 'run';
11044         opt = opt || {};
11045         var anim = Roo.lib.Anim[animType](
11046             this.dom, args,
11047             (opt.duration || defaultDur) || .35,
11048             (opt.easing || defaultEase) || 'easeOut',
11049             function(){
11050                 Roo.callback(cb, this);
11051             },
11052             this
11053         );
11054         opt.anim = anim;
11055         return anim;
11056     }
11057 };
11058
11059 // backwords compat
11060 Roo.Fx.resize = Roo.Fx.scale;
11061
11062 //When included, Roo.Fx is automatically applied to Element so that all basic
11063 //effects are available directly via the Element API
11064 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11065  * Based on:
11066  * Ext JS Library 1.1.1
11067  * Copyright(c) 2006-2007, Ext JS, LLC.
11068  *
11069  * Originally Released Under LGPL - original licence link has changed is not relivant.
11070  *
11071  * Fork - LGPL
11072  * <script type="text/javascript">
11073  */
11074
11075
11076 /**
11077  * @class Roo.CompositeElement
11078  * Standard composite class. Creates a Roo.Element for every element in the collection.
11079  * <br><br>
11080  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11081  * actions will be performed on all the elements in this collection.</b>
11082  * <br><br>
11083  * All methods return <i>this</i> and can be chained.
11084  <pre><code>
11085  var els = Roo.select("#some-el div.some-class", true);
11086  // or select directly from an existing element
11087  var el = Roo.get('some-el');
11088  el.select('div.some-class', true);
11089
11090  els.setWidth(100); // all elements become 100 width
11091  els.hide(true); // all elements fade out and hide
11092  // or
11093  els.setWidth(100).hide(true);
11094  </code></pre>
11095  */
11096 Roo.CompositeElement = function(els){
11097     this.elements = [];
11098     this.addElements(els);
11099 };
11100 Roo.CompositeElement.prototype = {
11101     isComposite: true,
11102     addElements : function(els){
11103         if(!els) {
11104             return this;
11105         }
11106         if(typeof els == "string"){
11107             els = Roo.Element.selectorFunction(els);
11108         }
11109         var yels = this.elements;
11110         var index = yels.length-1;
11111         for(var i = 0, len = els.length; i < len; i++) {
11112                 yels[++index] = Roo.get(els[i]);
11113         }
11114         return this;
11115     },
11116
11117     /**
11118     * Clears this composite and adds the elements returned by the passed selector.
11119     * @param {String/Array} els A string CSS selector, an array of elements or an element
11120     * @return {CompositeElement} this
11121     */
11122     fill : function(els){
11123         this.elements = [];
11124         this.add(els);
11125         return this;
11126     },
11127
11128     /**
11129     * Filters this composite to only elements that match the passed selector.
11130     * @param {String} selector A string CSS selector
11131     * @param {Boolean} inverse return inverse filter (not matches)
11132     * @return {CompositeElement} this
11133     */
11134     filter : function(selector, inverse){
11135         var els = [];
11136         inverse = inverse || false;
11137         this.each(function(el){
11138             var match = inverse ? !el.is(selector) : el.is(selector);
11139             if(match){
11140                 els[els.length] = el.dom;
11141             }
11142         });
11143         this.fill(els);
11144         return this;
11145     },
11146
11147     invoke : function(fn, args){
11148         var els = this.elements;
11149         for(var i = 0, len = els.length; i < len; i++) {
11150                 Roo.Element.prototype[fn].apply(els[i], args);
11151         }
11152         return this;
11153     },
11154     /**
11155     * Adds elements to this composite.
11156     * @param {String/Array} els A string CSS selector, an array of elements or an element
11157     * @return {CompositeElement} this
11158     */
11159     add : function(els){
11160         if(typeof els == "string"){
11161             this.addElements(Roo.Element.selectorFunction(els));
11162         }else if(els.length !== undefined){
11163             this.addElements(els);
11164         }else{
11165             this.addElements([els]);
11166         }
11167         return this;
11168     },
11169     /**
11170     * Calls the passed function passing (el, this, index) for each element in this composite.
11171     * @param {Function} fn The function to call
11172     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11173     * @return {CompositeElement} this
11174     */
11175     each : function(fn, scope){
11176         var els = this.elements;
11177         for(var i = 0, len = els.length; i < len; i++){
11178             if(fn.call(scope || els[i], els[i], this, i) === false) {
11179                 break;
11180             }
11181         }
11182         return this;
11183     },
11184
11185     /**
11186      * Returns the Element object at the specified index
11187      * @param {Number} index
11188      * @return {Roo.Element}
11189      */
11190     item : function(index){
11191         return this.elements[index] || null;
11192     },
11193
11194     /**
11195      * Returns the first Element
11196      * @return {Roo.Element}
11197      */
11198     first : function(){
11199         return this.item(0);
11200     },
11201
11202     /**
11203      * Returns the last Element
11204      * @return {Roo.Element}
11205      */
11206     last : function(){
11207         return this.item(this.elements.length-1);
11208     },
11209
11210     /**
11211      * Returns the number of elements in this composite
11212      * @return Number
11213      */
11214     getCount : function(){
11215         return this.elements.length;
11216     },
11217
11218     /**
11219      * Returns true if this composite contains the passed element
11220      * @return Boolean
11221      */
11222     contains : function(el){
11223         return this.indexOf(el) !== -1;
11224     },
11225
11226     /**
11227      * Returns true if this composite contains the passed element
11228      * @return Boolean
11229      */
11230     indexOf : function(el){
11231         return this.elements.indexOf(Roo.get(el));
11232     },
11233
11234
11235     /**
11236     * Removes the specified element(s).
11237     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11238     * or an array of any of those.
11239     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11240     * @return {CompositeElement} this
11241     */
11242     removeElement : function(el, removeDom){
11243         if(el instanceof Array){
11244             for(var i = 0, len = el.length; i < len; i++){
11245                 this.removeElement(el[i]);
11246             }
11247             return this;
11248         }
11249         var index = typeof el == 'number' ? el : this.indexOf(el);
11250         if(index !== -1){
11251             if(removeDom){
11252                 var d = this.elements[index];
11253                 if(d.dom){
11254                     d.remove();
11255                 }else{
11256                     d.parentNode.removeChild(d);
11257                 }
11258             }
11259             this.elements.splice(index, 1);
11260         }
11261         return this;
11262     },
11263
11264     /**
11265     * Replaces the specified element with the passed element.
11266     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11267     * to replace.
11268     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11269     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11270     * @return {CompositeElement} this
11271     */
11272     replaceElement : function(el, replacement, domReplace){
11273         var index = typeof el == 'number' ? el : this.indexOf(el);
11274         if(index !== -1){
11275             if(domReplace){
11276                 this.elements[index].replaceWith(replacement);
11277             }else{
11278                 this.elements.splice(index, 1, Roo.get(replacement))
11279             }
11280         }
11281         return this;
11282     },
11283
11284     /**
11285      * Removes all elements.
11286      */
11287     clear : function(){
11288         this.elements = [];
11289     }
11290 };
11291 (function(){
11292     Roo.CompositeElement.createCall = function(proto, fnName){
11293         if(!proto[fnName]){
11294             proto[fnName] = function(){
11295                 return this.invoke(fnName, arguments);
11296             };
11297         }
11298     };
11299     for(var fnName in Roo.Element.prototype){
11300         if(typeof Roo.Element.prototype[fnName] == "function"){
11301             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11302         }
11303     };
11304 })();
11305 /*
11306  * Based on:
11307  * Ext JS Library 1.1.1
11308  * Copyright(c) 2006-2007, Ext JS, LLC.
11309  *
11310  * Originally Released Under LGPL - original licence link has changed is not relivant.
11311  *
11312  * Fork - LGPL
11313  * <script type="text/javascript">
11314  */
11315
11316 /**
11317  * @class Roo.CompositeElementLite
11318  * @extends Roo.CompositeElement
11319  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11320  <pre><code>
11321  var els = Roo.select("#some-el div.some-class");
11322  // or select directly from an existing element
11323  var el = Roo.get('some-el');
11324  el.select('div.some-class');
11325
11326  els.setWidth(100); // all elements become 100 width
11327  els.hide(true); // all elements fade out and hide
11328  // or
11329  els.setWidth(100).hide(true);
11330  </code></pre><br><br>
11331  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11332  * actions will be performed on all the elements in this collection.</b>
11333  */
11334 Roo.CompositeElementLite = function(els){
11335     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11336     this.el = new Roo.Element.Flyweight();
11337 };
11338 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11339     addElements : function(els){
11340         if(els){
11341             if(els instanceof Array){
11342                 this.elements = this.elements.concat(els);
11343             }else{
11344                 var yels = this.elements;
11345                 var index = yels.length-1;
11346                 for(var i = 0, len = els.length; i < len; i++) {
11347                     yels[++index] = els[i];
11348                 }
11349             }
11350         }
11351         return this;
11352     },
11353     invoke : function(fn, args){
11354         var els = this.elements;
11355         var el = this.el;
11356         for(var i = 0, len = els.length; i < len; i++) {
11357             el.dom = els[i];
11358                 Roo.Element.prototype[fn].apply(el, args);
11359         }
11360         return this;
11361     },
11362     /**
11363      * Returns a flyweight Element of the dom element object at the specified index
11364      * @param {Number} index
11365      * @return {Roo.Element}
11366      */
11367     item : function(index){
11368         if(!this.elements[index]){
11369             return null;
11370         }
11371         this.el.dom = this.elements[index];
11372         return this.el;
11373     },
11374
11375     // fixes scope with flyweight
11376     addListener : function(eventName, handler, scope, opt){
11377         var els = this.elements;
11378         for(var i = 0, len = els.length; i < len; i++) {
11379             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11380         }
11381         return this;
11382     },
11383
11384     /**
11385     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11386     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11387     * a reference to the dom node, use el.dom.</b>
11388     * @param {Function} fn The function to call
11389     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11390     * @return {CompositeElement} this
11391     */
11392     each : function(fn, scope){
11393         var els = this.elements;
11394         var el = this.el;
11395         for(var i = 0, len = els.length; i < len; i++){
11396             el.dom = els[i];
11397                 if(fn.call(scope || el, el, this, i) === false){
11398                 break;
11399             }
11400         }
11401         return this;
11402     },
11403
11404     indexOf : function(el){
11405         return this.elements.indexOf(Roo.getDom(el));
11406     },
11407
11408     replaceElement : function(el, replacement, domReplace){
11409         var index = typeof el == 'number' ? el : this.indexOf(el);
11410         if(index !== -1){
11411             replacement = Roo.getDom(replacement);
11412             if(domReplace){
11413                 var d = this.elements[index];
11414                 d.parentNode.insertBefore(replacement, d);
11415                 d.parentNode.removeChild(d);
11416             }
11417             this.elements.splice(index, 1, replacement);
11418         }
11419         return this;
11420     }
11421 });
11422 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11423
11424 /*
11425  * Based on:
11426  * Ext JS Library 1.1.1
11427  * Copyright(c) 2006-2007, Ext JS, LLC.
11428  *
11429  * Originally Released Under LGPL - original licence link has changed is not relivant.
11430  *
11431  * Fork - LGPL
11432  * <script type="text/javascript">
11433  */
11434
11435  
11436
11437 /**
11438  * @class Roo.data.Connection
11439  * @extends Roo.util.Observable
11440  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11441  * either to a configured URL, or to a URL specified at request time.<br><br>
11442  * <p>
11443  * Requests made by this class are asynchronous, and will return immediately. No data from
11444  * the server will be available to the statement immediately following the {@link #request} call.
11445  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11446  * <p>
11447  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11448  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11449  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11450  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11451  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11452  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11453  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11454  * standard DOM methods.
11455  * @constructor
11456  * @param {Object} config a configuration object.
11457  */
11458 Roo.data.Connection = function(config){
11459     Roo.apply(this, config);
11460     this.addEvents({
11461         /**
11462          * @event beforerequest
11463          * Fires before a network request is made to retrieve a data object.
11464          * @param {Connection} conn This Connection object.
11465          * @param {Object} options The options config object passed to the {@link #request} method.
11466          */
11467         "beforerequest" : true,
11468         /**
11469          * @event requestcomplete
11470          * Fires if the request was successfully completed.
11471          * @param {Connection} conn This Connection object.
11472          * @param {Object} response The XHR object containing the response data.
11473          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11474          * @param {Object} options The options config object passed to the {@link #request} method.
11475          */
11476         "requestcomplete" : true,
11477         /**
11478          * @event requestexception
11479          * Fires if an error HTTP status was returned from the server.
11480          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11481          * @param {Connection} conn This Connection object.
11482          * @param {Object} response The XHR object containing the response data.
11483          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11484          * @param {Object} options The options config object passed to the {@link #request} method.
11485          */
11486         "requestexception" : true
11487     });
11488     Roo.data.Connection.superclass.constructor.call(this);
11489 };
11490
11491 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11492     /**
11493      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11494      */
11495     /**
11496      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11497      * extra parameters to each request made by this object. (defaults to undefined)
11498      */
11499     /**
11500      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11501      *  to each request made by this object. (defaults to undefined)
11502      */
11503     /**
11504      * @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)
11505      */
11506     /**
11507      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11508      */
11509     timeout : 30000,
11510     /**
11511      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11512      * @type Boolean
11513      */
11514     autoAbort:false,
11515
11516     /**
11517      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11518      * @type Boolean
11519      */
11520     disableCaching: true,
11521
11522     /**
11523      * Sends an HTTP request to a remote server.
11524      * @param {Object} options An object which may contain the following properties:<ul>
11525      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11526      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11527      * request, a url encoded string or a function to call to get either.</li>
11528      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11529      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11530      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11531      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11532      * <li>options {Object} The parameter to the request call.</li>
11533      * <li>success {Boolean} True if the request succeeded.</li>
11534      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11535      * </ul></li>
11536      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11537      * The callback is passed the following parameters:<ul>
11538      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11539      * <li>options {Object} The parameter to the request call.</li>
11540      * </ul></li>
11541      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11542      * The callback is passed the following parameters:<ul>
11543      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11544      * <li>options {Object} The parameter to the request call.</li>
11545      * </ul></li>
11546      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11547      * for the callback function. Defaults to the browser window.</li>
11548      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11549      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11550      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11551      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11552      * params for the post data. Any params will be appended to the URL.</li>
11553      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11554      * </ul>
11555      * @return {Number} transactionId
11556      */
11557     request : function(o){
11558         if(this.fireEvent("beforerequest", this, o) !== false){
11559             var p = o.params;
11560
11561             if(typeof p == "function"){
11562                 p = p.call(o.scope||window, o);
11563             }
11564             if(typeof p == "object"){
11565                 p = Roo.urlEncode(o.params);
11566             }
11567             if(this.extraParams){
11568                 var extras = Roo.urlEncode(this.extraParams);
11569                 p = p ? (p + '&' + extras) : extras;
11570             }
11571
11572             var url = o.url || this.url;
11573             if(typeof url == 'function'){
11574                 url = url.call(o.scope||window, o);
11575             }
11576
11577             if(o.form){
11578                 var form = Roo.getDom(o.form);
11579                 url = url || form.action;
11580
11581                 var enctype = form.getAttribute("enctype");
11582                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11583                     return this.doFormUpload(o, p, url);
11584                 }
11585                 var f = Roo.lib.Ajax.serializeForm(form);
11586                 p = p ? (p + '&' + f) : f;
11587             }
11588
11589             var hs = o.headers;
11590             if(this.defaultHeaders){
11591                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11592                 if(!o.headers){
11593                     o.headers = hs;
11594                 }
11595             }
11596
11597             var cb = {
11598                 success: this.handleResponse,
11599                 failure: this.handleFailure,
11600                 scope: this,
11601                 argument: {options: o},
11602                 timeout : o.timeout || this.timeout
11603             };
11604
11605             var method = o.method||this.method||(p ? "POST" : "GET");
11606
11607             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11608                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11609             }
11610
11611             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11612                 if(o.autoAbort){
11613                     this.abort();
11614                 }
11615             }else if(this.autoAbort !== false){
11616                 this.abort();
11617             }
11618
11619             if((method == 'GET' && p) || o.xmlData){
11620                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11621                 p = '';
11622             }
11623             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11624             return this.transId;
11625         }else{
11626             Roo.callback(o.callback, o.scope, [o, null, null]);
11627             return null;
11628         }
11629     },
11630
11631     /**
11632      * Determine whether this object has a request outstanding.
11633      * @param {Number} transactionId (Optional) defaults to the last transaction
11634      * @return {Boolean} True if there is an outstanding request.
11635      */
11636     isLoading : function(transId){
11637         if(transId){
11638             return Roo.lib.Ajax.isCallInProgress(transId);
11639         }else{
11640             return this.transId ? true : false;
11641         }
11642     },
11643
11644     /**
11645      * Aborts any outstanding request.
11646      * @param {Number} transactionId (Optional) defaults to the last transaction
11647      */
11648     abort : function(transId){
11649         if(transId || this.isLoading()){
11650             Roo.lib.Ajax.abort(transId || this.transId);
11651         }
11652     },
11653
11654     // private
11655     handleResponse : function(response){
11656         this.transId = false;
11657         var options = response.argument.options;
11658         response.argument = options ? options.argument : null;
11659         this.fireEvent("requestcomplete", this, response, options);
11660         Roo.callback(options.success, options.scope, [response, options]);
11661         Roo.callback(options.callback, options.scope, [options, true, response]);
11662     },
11663
11664     // private
11665     handleFailure : function(response, e){
11666         this.transId = false;
11667         var options = response.argument.options;
11668         response.argument = options ? options.argument : null;
11669         this.fireEvent("requestexception", this, response, options, e);
11670         Roo.callback(options.failure, options.scope, [response, options]);
11671         Roo.callback(options.callback, options.scope, [options, false, response]);
11672     },
11673
11674     // private
11675     doFormUpload : function(o, ps, url){
11676         var id = Roo.id();
11677         var frame = document.createElement('iframe');
11678         frame.id = id;
11679         frame.name = id;
11680         frame.className = 'x-hidden';
11681         if(Roo.isIE){
11682             frame.src = Roo.SSL_SECURE_URL;
11683         }
11684         document.body.appendChild(frame);
11685
11686         if(Roo.isIE){
11687            document.frames[id].name = id;
11688         }
11689
11690         var form = Roo.getDom(o.form);
11691         form.target = id;
11692         form.method = 'POST';
11693         form.enctype = form.encoding = 'multipart/form-data';
11694         if(url){
11695             form.action = url;
11696         }
11697
11698         var hiddens, hd;
11699         if(ps){ // add dynamic params
11700             hiddens = [];
11701             ps = Roo.urlDecode(ps, false);
11702             for(var k in ps){
11703                 if(ps.hasOwnProperty(k)){
11704                     hd = document.createElement('input');
11705                     hd.type = 'hidden';
11706                     hd.name = k;
11707                     hd.value = ps[k];
11708                     form.appendChild(hd);
11709                     hiddens.push(hd);
11710                 }
11711             }
11712         }
11713
11714         function cb(){
11715             var r = {  // bogus response object
11716                 responseText : '',
11717                 responseXML : null
11718             };
11719
11720             r.argument = o ? o.argument : null;
11721
11722             try { //
11723                 var doc;
11724                 if(Roo.isIE){
11725                     doc = frame.contentWindow.document;
11726                 }else {
11727                     doc = (frame.contentDocument || window.frames[id].document);
11728                 }
11729                 if(doc && doc.body){
11730                     r.responseText = doc.body.innerHTML;
11731                 }
11732                 if(doc && doc.XMLDocument){
11733                     r.responseXML = doc.XMLDocument;
11734                 }else {
11735                     r.responseXML = doc;
11736                 }
11737             }
11738             catch(e) {
11739                 // ignore
11740             }
11741
11742             Roo.EventManager.removeListener(frame, 'load', cb, this);
11743
11744             this.fireEvent("requestcomplete", this, r, o);
11745             Roo.callback(o.success, o.scope, [r, o]);
11746             Roo.callback(o.callback, o.scope, [o, true, r]);
11747
11748             setTimeout(function(){document.body.removeChild(frame);}, 100);
11749         }
11750
11751         Roo.EventManager.on(frame, 'load', cb, this);
11752         form.submit();
11753
11754         if(hiddens){ // remove dynamic params
11755             for(var i = 0, len = hiddens.length; i < len; i++){
11756                 form.removeChild(hiddens[i]);
11757             }
11758         }
11759     }
11760 });
11761 /*
11762  * Based on:
11763  * Ext JS Library 1.1.1
11764  * Copyright(c) 2006-2007, Ext JS, LLC.
11765  *
11766  * Originally Released Under LGPL - original licence link has changed is not relivant.
11767  *
11768  * Fork - LGPL
11769  * <script type="text/javascript">
11770  */
11771  
11772 /**
11773  * Global Ajax request class.
11774  * 
11775  * @class Roo.Ajax
11776  * @extends Roo.data.Connection
11777  * @static
11778  * 
11779  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11780  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11781  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11782  * @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)
11783  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11784  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11785  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11786  */
11787 Roo.Ajax = new Roo.data.Connection({
11788     // fix up the docs
11789     /**
11790      * @scope Roo.Ajax
11791      * @type {Boolear} 
11792      */
11793     autoAbort : false,
11794
11795     /**
11796      * Serialize the passed form into a url encoded string
11797      * @scope Roo.Ajax
11798      * @param {String/HTMLElement} form
11799      * @return {String}
11800      */
11801     serializeForm : function(form){
11802         return Roo.lib.Ajax.serializeForm(form);
11803     }
11804 });/*
11805  * Based on:
11806  * Ext JS Library 1.1.1
11807  * Copyright(c) 2006-2007, Ext JS, LLC.
11808  *
11809  * Originally Released Under LGPL - original licence link has changed is not relivant.
11810  *
11811  * Fork - LGPL
11812  * <script type="text/javascript">
11813  */
11814
11815  
11816 /**
11817  * @class Roo.UpdateManager
11818  * @extends Roo.util.Observable
11819  * Provides AJAX-style update for Element object.<br><br>
11820  * Usage:<br>
11821  * <pre><code>
11822  * // Get it from a Roo.Element object
11823  * var el = Roo.get("foo");
11824  * var mgr = el.getUpdateManager();
11825  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11826  * ...
11827  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11828  * <br>
11829  * // or directly (returns the same UpdateManager instance)
11830  * var mgr = new Roo.UpdateManager("myElementId");
11831  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11832  * mgr.on("update", myFcnNeedsToKnow);
11833  * <br>
11834    // short handed call directly from the element object
11835    Roo.get("foo").load({
11836         url: "bar.php",
11837         scripts:true,
11838         params: "for=bar",
11839         text: "Loading Foo..."
11840    });
11841  * </code></pre>
11842  * @constructor
11843  * Create new UpdateManager directly.
11844  * @param {String/HTMLElement/Roo.Element} el The element to update
11845  * @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).
11846  */
11847 Roo.UpdateManager = function(el, forceNew){
11848     el = Roo.get(el);
11849     if(!forceNew && el.updateManager){
11850         return el.updateManager;
11851     }
11852     /**
11853      * The Element object
11854      * @type Roo.Element
11855      */
11856     this.el = el;
11857     /**
11858      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11859      * @type String
11860      */
11861     this.defaultUrl = null;
11862
11863     this.addEvents({
11864         /**
11865          * @event beforeupdate
11866          * Fired before an update is made, return false from your handler and the update is cancelled.
11867          * @param {Roo.Element} el
11868          * @param {String/Object/Function} url
11869          * @param {String/Object} params
11870          */
11871         "beforeupdate": true,
11872         /**
11873          * @event update
11874          * Fired after successful update is made.
11875          * @param {Roo.Element} el
11876          * @param {Object} oResponseObject The response Object
11877          */
11878         "update": true,
11879         /**
11880          * @event failure
11881          * Fired on update failure.
11882          * @param {Roo.Element} el
11883          * @param {Object} oResponseObject The response Object
11884          */
11885         "failure": true
11886     });
11887     var d = Roo.UpdateManager.defaults;
11888     /**
11889      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11890      * @type String
11891      */
11892     this.sslBlankUrl = d.sslBlankUrl;
11893     /**
11894      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11895      * @type Boolean
11896      */
11897     this.disableCaching = d.disableCaching;
11898     /**
11899      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11900      * @type String
11901      */
11902     this.indicatorText = d.indicatorText;
11903     /**
11904      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11905      * @type String
11906      */
11907     this.showLoadIndicator = d.showLoadIndicator;
11908     /**
11909      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11910      * @type Number
11911      */
11912     this.timeout = d.timeout;
11913
11914     /**
11915      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11916      * @type Boolean
11917      */
11918     this.loadScripts = d.loadScripts;
11919
11920     /**
11921      * Transaction object of current executing transaction
11922      */
11923     this.transaction = null;
11924
11925     /**
11926      * @private
11927      */
11928     this.autoRefreshProcId = null;
11929     /**
11930      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11931      * @type Function
11932      */
11933     this.refreshDelegate = this.refresh.createDelegate(this);
11934     /**
11935      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11936      * @type Function
11937      */
11938     this.updateDelegate = this.update.createDelegate(this);
11939     /**
11940      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11941      * @type Function
11942      */
11943     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11944     /**
11945      * @private
11946      */
11947     this.successDelegate = this.processSuccess.createDelegate(this);
11948     /**
11949      * @private
11950      */
11951     this.failureDelegate = this.processFailure.createDelegate(this);
11952
11953     if(!this.renderer){
11954      /**
11955       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11956       */
11957     this.renderer = new Roo.UpdateManager.BasicRenderer();
11958     }
11959     
11960     Roo.UpdateManager.superclass.constructor.call(this);
11961 };
11962
11963 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11964     /**
11965      * Get the Element this UpdateManager is bound to
11966      * @return {Roo.Element} The element
11967      */
11968     getEl : function(){
11969         return this.el;
11970     },
11971     /**
11972      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11973      * @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:
11974 <pre><code>
11975 um.update({<br/>
11976     url: "your-url.php",<br/>
11977     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11978     callback: yourFunction,<br/>
11979     scope: yourObject, //(optional scope)  <br/>
11980     discardUrl: false, <br/>
11981     nocache: false,<br/>
11982     text: "Loading...",<br/>
11983     timeout: 30,<br/>
11984     scripts: false<br/>
11985 });
11986 </code></pre>
11987      * The only required property is url. The optional properties nocache, text and scripts
11988      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11989      * @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}
11990      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11991      * @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.
11992      */
11993     update : function(url, params, callback, discardUrl){
11994         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11995             var method = this.method,
11996                 cfg;
11997             if(typeof url == "object"){ // must be config object
11998                 cfg = url;
11999                 url = cfg.url;
12000                 params = params || cfg.params;
12001                 callback = callback || cfg.callback;
12002                 discardUrl = discardUrl || cfg.discardUrl;
12003                 if(callback && cfg.scope){
12004                     callback = callback.createDelegate(cfg.scope);
12005                 }
12006                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12007                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12008                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12009                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12010                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12011             }
12012             this.showLoading();
12013             if(!discardUrl){
12014                 this.defaultUrl = url;
12015             }
12016             if(typeof url == "function"){
12017                 url = url.call(this);
12018             }
12019
12020             method = method || (params ? "POST" : "GET");
12021             if(method == "GET"){
12022                 url = this.prepareUrl(url);
12023             }
12024
12025             var o = Roo.apply(cfg ||{}, {
12026                 url : url,
12027                 params: params,
12028                 success: this.successDelegate,
12029                 failure: this.failureDelegate,
12030                 callback: undefined,
12031                 timeout: (this.timeout*1000),
12032                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12033             });
12034             Roo.log("updated manager called with timeout of " + o.timeout);
12035             this.transaction = Roo.Ajax.request(o);
12036         }
12037     },
12038
12039     /**
12040      * 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.
12041      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12042      * @param {String/HTMLElement} form The form Id or form element
12043      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12044      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12045      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12046      */
12047     formUpdate : function(form, url, reset, callback){
12048         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12049             if(typeof url == "function"){
12050                 url = url.call(this);
12051             }
12052             form = Roo.getDom(form);
12053             this.transaction = Roo.Ajax.request({
12054                 form: form,
12055                 url:url,
12056                 success: this.successDelegate,
12057                 failure: this.failureDelegate,
12058                 timeout: (this.timeout*1000),
12059                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12060             });
12061             this.showLoading.defer(1, this);
12062         }
12063     },
12064
12065     /**
12066      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12067      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12068      */
12069     refresh : function(callback){
12070         if(this.defaultUrl == null){
12071             return;
12072         }
12073         this.update(this.defaultUrl, null, callback, true);
12074     },
12075
12076     /**
12077      * Set this element to auto refresh.
12078      * @param {Number} interval How often to update (in seconds).
12079      * @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)
12080      * @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}
12081      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12082      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12083      */
12084     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12085         if(refreshNow){
12086             this.update(url || this.defaultUrl, params, callback, true);
12087         }
12088         if(this.autoRefreshProcId){
12089             clearInterval(this.autoRefreshProcId);
12090         }
12091         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12092     },
12093
12094     /**
12095      * Stop auto refresh on this element.
12096      */
12097      stopAutoRefresh : function(){
12098         if(this.autoRefreshProcId){
12099             clearInterval(this.autoRefreshProcId);
12100             delete this.autoRefreshProcId;
12101         }
12102     },
12103
12104     isAutoRefreshing : function(){
12105        return this.autoRefreshProcId ? true : false;
12106     },
12107     /**
12108      * Called to update the element to "Loading" state. Override to perform custom action.
12109      */
12110     showLoading : function(){
12111         if(this.showLoadIndicator){
12112             this.el.update(this.indicatorText);
12113         }
12114     },
12115
12116     /**
12117      * Adds unique parameter to query string if disableCaching = true
12118      * @private
12119      */
12120     prepareUrl : function(url){
12121         if(this.disableCaching){
12122             var append = "_dc=" + (new Date().getTime());
12123             if(url.indexOf("?") !== -1){
12124                 url += "&" + append;
12125             }else{
12126                 url += "?" + append;
12127             }
12128         }
12129         return url;
12130     },
12131
12132     /**
12133      * @private
12134      */
12135     processSuccess : function(response){
12136         this.transaction = null;
12137         if(response.argument.form && response.argument.reset){
12138             try{ // put in try/catch since some older FF releases had problems with this
12139                 response.argument.form.reset();
12140             }catch(e){}
12141         }
12142         if(this.loadScripts){
12143             this.renderer.render(this.el, response, this,
12144                 this.updateComplete.createDelegate(this, [response]));
12145         }else{
12146             this.renderer.render(this.el, response, this);
12147             this.updateComplete(response);
12148         }
12149     },
12150
12151     updateComplete : function(response){
12152         this.fireEvent("update", this.el, response);
12153         if(typeof response.argument.callback == "function"){
12154             response.argument.callback(this.el, true, response);
12155         }
12156     },
12157
12158     /**
12159      * @private
12160      */
12161     processFailure : function(response){
12162         this.transaction = null;
12163         this.fireEvent("failure", this.el, response);
12164         if(typeof response.argument.callback == "function"){
12165             response.argument.callback(this.el, false, response);
12166         }
12167     },
12168
12169     /**
12170      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12171      * @param {Object} renderer The object implementing the render() method
12172      */
12173     setRenderer : function(renderer){
12174         this.renderer = renderer;
12175     },
12176
12177     getRenderer : function(){
12178        return this.renderer;
12179     },
12180
12181     /**
12182      * Set the defaultUrl used for updates
12183      * @param {String/Function} defaultUrl The url or a function to call to get the url
12184      */
12185     setDefaultUrl : function(defaultUrl){
12186         this.defaultUrl = defaultUrl;
12187     },
12188
12189     /**
12190      * Aborts the executing transaction
12191      */
12192     abort : function(){
12193         if(this.transaction){
12194             Roo.Ajax.abort(this.transaction);
12195         }
12196     },
12197
12198     /**
12199      * Returns true if an update is in progress
12200      * @return {Boolean}
12201      */
12202     isUpdating : function(){
12203         if(this.transaction){
12204             return Roo.Ajax.isLoading(this.transaction);
12205         }
12206         return false;
12207     }
12208 });
12209
12210 /**
12211  * @class Roo.UpdateManager.defaults
12212  * @static (not really - but it helps the doc tool)
12213  * The defaults collection enables customizing the default properties of UpdateManager
12214  */
12215    Roo.UpdateManager.defaults = {
12216        /**
12217          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12218          * @type Number
12219          */
12220          timeout : 30,
12221
12222          /**
12223          * True to process scripts by default (Defaults to false).
12224          * @type Boolean
12225          */
12226         loadScripts : false,
12227
12228         /**
12229         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12230         * @type String
12231         */
12232         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12233         /**
12234          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12235          * @type Boolean
12236          */
12237         disableCaching : false,
12238         /**
12239          * Whether to show indicatorText when loading (Defaults to true).
12240          * @type Boolean
12241          */
12242         showLoadIndicator : true,
12243         /**
12244          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12245          * @type String
12246          */
12247         indicatorText : '<div class="loading-indicator">Loading...</div>'
12248    };
12249
12250 /**
12251  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12252  *Usage:
12253  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12254  * @param {String/HTMLElement/Roo.Element} el The element to update
12255  * @param {String} url The url
12256  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12257  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12258  * @static
12259  * @deprecated
12260  * @member Roo.UpdateManager
12261  */
12262 Roo.UpdateManager.updateElement = function(el, url, params, options){
12263     var um = Roo.get(el, true).getUpdateManager();
12264     Roo.apply(um, options);
12265     um.update(url, params, options ? options.callback : null);
12266 };
12267 // alias for backwards compat
12268 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12269 /**
12270  * @class Roo.UpdateManager.BasicRenderer
12271  * Default Content renderer. Updates the elements innerHTML with the responseText.
12272  */
12273 Roo.UpdateManager.BasicRenderer = function(){};
12274
12275 Roo.UpdateManager.BasicRenderer.prototype = {
12276     /**
12277      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12278      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12279      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12280      * @param {Roo.Element} el The element being rendered
12281      * @param {Object} response The YUI Connect response object
12282      * @param {UpdateManager} updateManager The calling update manager
12283      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12284      */
12285      render : function(el, response, updateManager, callback){
12286         el.update(response.responseText, updateManager.loadScripts, callback);
12287     }
12288 };
12289 /*
12290  * Based on:
12291  * Roo JS
12292  * (c)) Alan Knowles
12293  * Licence : LGPL
12294  */
12295
12296
12297 /**
12298  * @class Roo.DomTemplate
12299  * @extends Roo.Template
12300  * An effort at a dom based template engine..
12301  *
12302  * Similar to XTemplate, except it uses dom parsing to create the template..
12303  *
12304  * Supported features:
12305  *
12306  *  Tags:
12307
12308 <pre><code>
12309       {a_variable} - output encoded.
12310       {a_variable.format:("Y-m-d")} - call a method on the variable
12311       {a_variable:raw} - unencoded output
12312       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12313       {a_variable:this.method_on_template(...)} - call a method on the template object.
12314  
12315 </code></pre>
12316  *  The tpl tag:
12317 <pre><code>
12318         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12319         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12320         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12321         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12322   
12323 </code></pre>
12324  *      
12325  */
12326 Roo.DomTemplate = function()
12327 {
12328      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12329      if (this.html) {
12330         this.compile();
12331      }
12332 };
12333
12334
12335 Roo.extend(Roo.DomTemplate, Roo.Template, {
12336     /**
12337      * id counter for sub templates.
12338      */
12339     id : 0,
12340     /**
12341      * flag to indicate if dom parser is inside a pre,
12342      * it will strip whitespace if not.
12343      */
12344     inPre : false,
12345     
12346     /**
12347      * The various sub templates
12348      */
12349     tpls : false,
12350     
12351     
12352     
12353     /**
12354      *
12355      * basic tag replacing syntax
12356      * WORD:WORD()
12357      *
12358      * // you can fake an object call by doing this
12359      *  x.t:(test,tesT) 
12360      * 
12361      */
12362     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12363     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12364     
12365     iterChild : function (node, method) {
12366         
12367         var oldPre = this.inPre;
12368         if (node.tagName == 'PRE') {
12369             this.inPre = true;
12370         }
12371         for( var i = 0; i < node.childNodes.length; i++) {
12372             method.call(this, node.childNodes[i]);
12373         }
12374         this.inPre = oldPre;
12375     },
12376     
12377     
12378     
12379     /**
12380      * compile the template
12381      *
12382      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12383      *
12384      */
12385     compile: function()
12386     {
12387         var s = this.html;
12388         
12389         // covert the html into DOM...
12390         var doc = false;
12391         var div =false;
12392         try {
12393             doc = document.implementation.createHTMLDocument("");
12394             doc.documentElement.innerHTML =   this.html  ;
12395             div = doc.documentElement;
12396         } catch (e) {
12397             // old IE... - nasty -- it causes all sorts of issues.. with
12398             // images getting pulled from server..
12399             div = document.createElement('div');
12400             div.innerHTML = this.html;
12401         }
12402         //doc.documentElement.innerHTML = htmlBody
12403          
12404         
12405         
12406         this.tpls = [];
12407         var _t = this;
12408         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12409         
12410         var tpls = this.tpls;
12411         
12412         // create a top level template from the snippet..
12413         
12414         //Roo.log(div.innerHTML);
12415         
12416         var tpl = {
12417             uid : 'master',
12418             id : this.id++,
12419             attr : false,
12420             value : false,
12421             body : div.innerHTML,
12422             
12423             forCall : false,
12424             execCall : false,
12425             dom : div,
12426             isTop : true
12427             
12428         };
12429         tpls.unshift(tpl);
12430         
12431         
12432         // compile them...
12433         this.tpls = [];
12434         Roo.each(tpls, function(tp){
12435             this.compileTpl(tp);
12436             this.tpls[tp.id] = tp;
12437         }, this);
12438         
12439         this.master = tpls[0];
12440         return this;
12441         
12442         
12443     },
12444     
12445     compileNode : function(node, istop) {
12446         // test for
12447         //Roo.log(node);
12448         
12449         
12450         // skip anything not a tag..
12451         if (node.nodeType != 1) {
12452             if (node.nodeType == 3 && !this.inPre) {
12453                 // reduce white space..
12454                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12455                 
12456             }
12457             return;
12458         }
12459         
12460         var tpl = {
12461             uid : false,
12462             id : false,
12463             attr : false,
12464             value : false,
12465             body : '',
12466             
12467             forCall : false,
12468             execCall : false,
12469             dom : false,
12470             isTop : istop
12471             
12472             
12473         };
12474         
12475         
12476         switch(true) {
12477             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12478             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12479             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12480             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12481             // no default..
12482         }
12483         
12484         
12485         if (!tpl.attr) {
12486             // just itterate children..
12487             this.iterChild(node,this.compileNode);
12488             return;
12489         }
12490         tpl.uid = this.id++;
12491         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12492         node.removeAttribute('roo-'+ tpl.attr);
12493         if (tpl.attr != 'name') {
12494             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12495             node.parentNode.replaceChild(placeholder,  node);
12496         } else {
12497             
12498             var placeholder =  document.createElement('span');
12499             placeholder.className = 'roo-tpl-' + tpl.value;
12500             node.parentNode.replaceChild(placeholder,  node);
12501         }
12502         
12503         // parent now sees '{domtplXXXX}
12504         this.iterChild(node,this.compileNode);
12505         
12506         // we should now have node body...
12507         var div = document.createElement('div');
12508         div.appendChild(node);
12509         tpl.dom = node;
12510         // this has the unfortunate side effect of converting tagged attributes
12511         // eg. href="{...}" into %7C...%7D
12512         // this has been fixed by searching for those combo's although it's a bit hacky..
12513         
12514         
12515         tpl.body = div.innerHTML;
12516         
12517         
12518          
12519         tpl.id = tpl.uid;
12520         switch(tpl.attr) {
12521             case 'for' :
12522                 switch (tpl.value) {
12523                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12524                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12525                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12526                 }
12527                 break;
12528             
12529             case 'exec':
12530                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12531                 break;
12532             
12533             case 'if':     
12534                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12535                 break;
12536             
12537             case 'name':
12538                 tpl.id  = tpl.value; // replace non characters???
12539                 break;
12540             
12541         }
12542         
12543         
12544         this.tpls.push(tpl);
12545         
12546         
12547         
12548     },
12549     
12550     
12551     
12552     
12553     /**
12554      * Compile a segment of the template into a 'sub-template'
12555      *
12556      * 
12557      * 
12558      *
12559      */
12560     compileTpl : function(tpl)
12561     {
12562         var fm = Roo.util.Format;
12563         var useF = this.disableFormats !== true;
12564         
12565         var sep = Roo.isGecko ? "+\n" : ",\n";
12566         
12567         var undef = function(str) {
12568             Roo.debug && Roo.log("Property not found :"  + str);
12569             return '';
12570         };
12571           
12572         //Roo.log(tpl.body);
12573         
12574         
12575         
12576         var fn = function(m, lbrace, name, format, args)
12577         {
12578             //Roo.log("ARGS");
12579             //Roo.log(arguments);
12580             args = args ? args.replace(/\\'/g,"'") : args;
12581             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12582             if (typeof(format) == 'undefined') {
12583                 format =  'htmlEncode'; 
12584             }
12585             if (format == 'raw' ) {
12586                 format = false;
12587             }
12588             
12589             if(name.substr(0, 6) == 'domtpl'){
12590                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12591             }
12592             
12593             // build an array of options to determine if value is undefined..
12594             
12595             // basically get 'xxxx.yyyy' then do
12596             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12597             //    (function () { Roo.log("Property not found"); return ''; })() :
12598             //    ......
12599             
12600             var udef_ar = [];
12601             var lookfor = '';
12602             Roo.each(name.split('.'), function(st) {
12603                 lookfor += (lookfor.length ? '.': '') + st;
12604                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12605             });
12606             
12607             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12608             
12609             
12610             if(format && useF){
12611                 
12612                 args = args ? ',' + args : "";
12613                  
12614                 if(format.substr(0, 5) != "this."){
12615                     format = "fm." + format + '(';
12616                 }else{
12617                     format = 'this.call("'+ format.substr(5) + '", ';
12618                     args = ", values";
12619                 }
12620                 
12621                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12622             }
12623              
12624             if (args && args.length) {
12625                 // called with xxyx.yuu:(test,test)
12626                 // change to ()
12627                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12628             }
12629             // raw.. - :raw modifier..
12630             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12631             
12632         };
12633         var body;
12634         // branched to use + in gecko and [].join() in others
12635         if(Roo.isGecko){
12636             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12637                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12638                     "';};};";
12639         }else{
12640             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12641             body.push(tpl.body.replace(/(\r\n|\n)/g,
12642                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12643             body.push("'].join('');};};");
12644             body = body.join('');
12645         }
12646         
12647         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12648        
12649         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12650         eval(body);
12651         
12652         return this;
12653     },
12654      
12655     /**
12656      * same as applyTemplate, except it's done to one of the subTemplates
12657      * when using named templates, you can do:
12658      *
12659      * var str = pl.applySubTemplate('your-name', values);
12660      *
12661      * 
12662      * @param {Number} id of the template
12663      * @param {Object} values to apply to template
12664      * @param {Object} parent (normaly the instance of this object)
12665      */
12666     applySubTemplate : function(id, values, parent)
12667     {
12668         
12669         
12670         var t = this.tpls[id];
12671         
12672         
12673         try { 
12674             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12675                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12676                 return '';
12677             }
12678         } catch(e) {
12679             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12680             Roo.log(values);
12681           
12682             return '';
12683         }
12684         try { 
12685             
12686             if(t.execCall && t.execCall.call(this, values, parent)){
12687                 return '';
12688             }
12689         } catch(e) {
12690             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12691             Roo.log(values);
12692             return '';
12693         }
12694         
12695         try {
12696             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12697             parent = t.target ? values : parent;
12698             if(t.forCall && vs instanceof Array){
12699                 var buf = [];
12700                 for(var i = 0, len = vs.length; i < len; i++){
12701                     try {
12702                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12703                     } catch (e) {
12704                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12705                         Roo.log(e.body);
12706                         //Roo.log(t.compiled);
12707                         Roo.log(vs[i]);
12708                     }   
12709                 }
12710                 return buf.join('');
12711             }
12712         } catch (e) {
12713             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12714             Roo.log(values);
12715             return '';
12716         }
12717         try {
12718             return t.compiled.call(this, vs, parent);
12719         } catch (e) {
12720             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12721             Roo.log(e.body);
12722             //Roo.log(t.compiled);
12723             Roo.log(values);
12724             return '';
12725         }
12726     },
12727
12728    
12729
12730     applyTemplate : function(values){
12731         return this.master.compiled.call(this, values, {});
12732         //var s = this.subs;
12733     },
12734
12735     apply : function(){
12736         return this.applyTemplate.apply(this, arguments);
12737     }
12738
12739  });
12740
12741 Roo.DomTemplate.from = function(el){
12742     el = Roo.getDom(el);
12743     return new Roo.Domtemplate(el.value || el.innerHTML);
12744 };/*
12745  * Based on:
12746  * Ext JS Library 1.1.1
12747  * Copyright(c) 2006-2007, Ext JS, LLC.
12748  *
12749  * Originally Released Under LGPL - original licence link has changed is not relivant.
12750  *
12751  * Fork - LGPL
12752  * <script type="text/javascript">
12753  */
12754
12755 /**
12756  * @class Roo.util.DelayedTask
12757  * Provides a convenient method of performing setTimeout where a new
12758  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12759  * You can use this class to buffer
12760  * the keypress events for a certain number of milliseconds, and perform only if they stop
12761  * for that amount of time.
12762  * @constructor The parameters to this constructor serve as defaults and are not required.
12763  * @param {Function} fn (optional) The default function to timeout
12764  * @param {Object} scope (optional) The default scope of that timeout
12765  * @param {Array} args (optional) The default Array of arguments
12766  */
12767 Roo.util.DelayedTask = function(fn, scope, args){
12768     var id = null, d, t;
12769
12770     var call = function(){
12771         var now = new Date().getTime();
12772         if(now - t >= d){
12773             clearInterval(id);
12774             id = null;
12775             fn.apply(scope, args || []);
12776         }
12777     };
12778     /**
12779      * Cancels any pending timeout and queues a new one
12780      * @param {Number} delay The milliseconds to delay
12781      * @param {Function} newFn (optional) Overrides function passed to constructor
12782      * @param {Object} newScope (optional) Overrides scope passed to constructor
12783      * @param {Array} newArgs (optional) Overrides args passed to constructor
12784      */
12785     this.delay = function(delay, newFn, newScope, newArgs){
12786         if(id && delay != d){
12787             this.cancel();
12788         }
12789         d = delay;
12790         t = new Date().getTime();
12791         fn = newFn || fn;
12792         scope = newScope || scope;
12793         args = newArgs || args;
12794         if(!id){
12795             id = setInterval(call, d);
12796         }
12797     };
12798
12799     /**
12800      * Cancel the last queued timeout
12801      */
12802     this.cancel = function(){
12803         if(id){
12804             clearInterval(id);
12805             id = null;
12806         }
12807     };
12808 };/*
12809  * Based on:
12810  * Ext JS Library 1.1.1
12811  * Copyright(c) 2006-2007, Ext JS, LLC.
12812  *
12813  * Originally Released Under LGPL - original licence link has changed is not relivant.
12814  *
12815  * Fork - LGPL
12816  * <script type="text/javascript">
12817  */
12818  
12819  
12820 Roo.util.TaskRunner = function(interval){
12821     interval = interval || 10;
12822     var tasks = [], removeQueue = [];
12823     var id = 0;
12824     var running = false;
12825
12826     var stopThread = function(){
12827         running = false;
12828         clearInterval(id);
12829         id = 0;
12830     };
12831
12832     var startThread = function(){
12833         if(!running){
12834             running = true;
12835             id = setInterval(runTasks, interval);
12836         }
12837     };
12838
12839     var removeTask = function(task){
12840         removeQueue.push(task);
12841         if(task.onStop){
12842             task.onStop();
12843         }
12844     };
12845
12846     var runTasks = function(){
12847         if(removeQueue.length > 0){
12848             for(var i = 0, len = removeQueue.length; i < len; i++){
12849                 tasks.remove(removeQueue[i]);
12850             }
12851             removeQueue = [];
12852             if(tasks.length < 1){
12853                 stopThread();
12854                 return;
12855             }
12856         }
12857         var now = new Date().getTime();
12858         for(var i = 0, len = tasks.length; i < len; ++i){
12859             var t = tasks[i];
12860             var itime = now - t.taskRunTime;
12861             if(t.interval <= itime){
12862                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12863                 t.taskRunTime = now;
12864                 if(rt === false || t.taskRunCount === t.repeat){
12865                     removeTask(t);
12866                     return;
12867                 }
12868             }
12869             if(t.duration && t.duration <= (now - t.taskStartTime)){
12870                 removeTask(t);
12871             }
12872         }
12873     };
12874
12875     /**
12876      * Queues a new task.
12877      * @param {Object} task
12878      */
12879     this.start = function(task){
12880         tasks.push(task);
12881         task.taskStartTime = new Date().getTime();
12882         task.taskRunTime = 0;
12883         task.taskRunCount = 0;
12884         startThread();
12885         return task;
12886     };
12887
12888     this.stop = function(task){
12889         removeTask(task);
12890         return task;
12891     };
12892
12893     this.stopAll = function(){
12894         stopThread();
12895         for(var i = 0, len = tasks.length; i < len; i++){
12896             if(tasks[i].onStop){
12897                 tasks[i].onStop();
12898             }
12899         }
12900         tasks = [];
12901         removeQueue = [];
12902     };
12903 };
12904
12905 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12906  * Based on:
12907  * Ext JS Library 1.1.1
12908  * Copyright(c) 2006-2007, Ext JS, LLC.
12909  *
12910  * Originally Released Under LGPL - original licence link has changed is not relivant.
12911  *
12912  * Fork - LGPL
12913  * <script type="text/javascript">
12914  */
12915
12916  
12917 /**
12918  * @class Roo.util.MixedCollection
12919  * @extends Roo.util.Observable
12920  * A Collection class that maintains both numeric indexes and keys and exposes events.
12921  * @constructor
12922  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12923  * collection (defaults to false)
12924  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12925  * and return the key value for that item.  This is used when available to look up the key on items that
12926  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12927  * equivalent to providing an implementation for the {@link #getKey} method.
12928  */
12929 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12930     this.items = [];
12931     this.map = {};
12932     this.keys = [];
12933     this.length = 0;
12934     this.addEvents({
12935         /**
12936          * @event clear
12937          * Fires when the collection is cleared.
12938          */
12939         "clear" : true,
12940         /**
12941          * @event add
12942          * Fires when an item is added to the collection.
12943          * @param {Number} index The index at which the item was added.
12944          * @param {Object} o The item added.
12945          * @param {String} key The key associated with the added item.
12946          */
12947         "add" : true,
12948         /**
12949          * @event replace
12950          * Fires when an item is replaced in the collection.
12951          * @param {String} key he key associated with the new added.
12952          * @param {Object} old The item being replaced.
12953          * @param {Object} new The new item.
12954          */
12955         "replace" : true,
12956         /**
12957          * @event remove
12958          * Fires when an item is removed from the collection.
12959          * @param {Object} o The item being removed.
12960          * @param {String} key (optional) The key associated with the removed item.
12961          */
12962         "remove" : true,
12963         "sort" : true
12964     });
12965     this.allowFunctions = allowFunctions === true;
12966     if(keyFn){
12967         this.getKey = keyFn;
12968     }
12969     Roo.util.MixedCollection.superclass.constructor.call(this);
12970 };
12971
12972 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12973     allowFunctions : false,
12974     
12975 /**
12976  * Adds an item to the collection.
12977  * @param {String} key The key to associate with the item
12978  * @param {Object} o The item to add.
12979  * @return {Object} The item added.
12980  */
12981     add : function(key, o){
12982         if(arguments.length == 1){
12983             o = arguments[0];
12984             key = this.getKey(o);
12985         }
12986         if(typeof key == "undefined" || key === null){
12987             this.length++;
12988             this.items.push(o);
12989             this.keys.push(null);
12990         }else{
12991             var old = this.map[key];
12992             if(old){
12993                 return this.replace(key, o);
12994             }
12995             this.length++;
12996             this.items.push(o);
12997             this.map[key] = o;
12998             this.keys.push(key);
12999         }
13000         this.fireEvent("add", this.length-1, o, key);
13001         return o;
13002     },
13003        
13004 /**
13005   * MixedCollection has a generic way to fetch keys if you implement getKey.
13006 <pre><code>
13007 // normal way
13008 var mc = new Roo.util.MixedCollection();
13009 mc.add(someEl.dom.id, someEl);
13010 mc.add(otherEl.dom.id, otherEl);
13011 //and so on
13012
13013 // using getKey
13014 var mc = new Roo.util.MixedCollection();
13015 mc.getKey = function(el){
13016    return el.dom.id;
13017 };
13018 mc.add(someEl);
13019 mc.add(otherEl);
13020
13021 // or via the constructor
13022 var mc = new Roo.util.MixedCollection(false, function(el){
13023    return el.dom.id;
13024 });
13025 mc.add(someEl);
13026 mc.add(otherEl);
13027 </code></pre>
13028  * @param o {Object} The item for which to find the key.
13029  * @return {Object} The key for the passed item.
13030  */
13031     getKey : function(o){
13032          return o.id; 
13033     },
13034    
13035 /**
13036  * Replaces an item in the collection.
13037  * @param {String} key The key associated with the item to replace, or the item to replace.
13038  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13039  * @return {Object}  The new item.
13040  */
13041     replace : function(key, o){
13042         if(arguments.length == 1){
13043             o = arguments[0];
13044             key = this.getKey(o);
13045         }
13046         var old = this.item(key);
13047         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13048              return this.add(key, o);
13049         }
13050         var index = this.indexOfKey(key);
13051         this.items[index] = o;
13052         this.map[key] = o;
13053         this.fireEvent("replace", key, old, o);
13054         return o;
13055     },
13056    
13057 /**
13058  * Adds all elements of an Array or an Object to the collection.
13059  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13060  * an Array of values, each of which are added to the collection.
13061  */
13062     addAll : function(objs){
13063         if(arguments.length > 1 || objs instanceof Array){
13064             var args = arguments.length > 1 ? arguments : objs;
13065             for(var i = 0, len = args.length; i < len; i++){
13066                 this.add(args[i]);
13067             }
13068         }else{
13069             for(var key in objs){
13070                 if(this.allowFunctions || typeof objs[key] != "function"){
13071                     this.add(key, objs[key]);
13072                 }
13073             }
13074         }
13075     },
13076    
13077 /**
13078  * Executes the specified function once for every item in the collection, passing each
13079  * item as the first and only parameter. returning false from the function will stop the iteration.
13080  * @param {Function} fn The function to execute for each item.
13081  * @param {Object} scope (optional) The scope in which to execute the function.
13082  */
13083     each : function(fn, scope){
13084         var items = [].concat(this.items); // each safe for removal
13085         for(var i = 0, len = items.length; i < len; i++){
13086             if(fn.call(scope || items[i], items[i], i, len) === false){
13087                 break;
13088             }
13089         }
13090     },
13091    
13092 /**
13093  * Executes the specified function once for every key in the collection, passing each
13094  * key, and its associated item as the first two parameters.
13095  * @param {Function} fn The function to execute for each item.
13096  * @param {Object} scope (optional) The scope in which to execute the function.
13097  */
13098     eachKey : function(fn, scope){
13099         for(var i = 0, len = this.keys.length; i < len; i++){
13100             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13101         }
13102     },
13103    
13104 /**
13105  * Returns the first item in the collection which elicits a true return value from the
13106  * passed selection function.
13107  * @param {Function} fn The selection function to execute for each item.
13108  * @param {Object} scope (optional) The scope in which to execute the function.
13109  * @return {Object} The first item in the collection which returned true from the selection function.
13110  */
13111     find : function(fn, scope){
13112         for(var i = 0, len = this.items.length; i < len; i++){
13113             if(fn.call(scope || window, this.items[i], this.keys[i])){
13114                 return this.items[i];
13115             }
13116         }
13117         return null;
13118     },
13119    
13120 /**
13121  * Inserts an item at the specified index in the collection.
13122  * @param {Number} index The index to insert the item at.
13123  * @param {String} key The key to associate with the new item, or the item itself.
13124  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13125  * @return {Object} The item inserted.
13126  */
13127     insert : function(index, key, o){
13128         if(arguments.length == 2){
13129             o = arguments[1];
13130             key = this.getKey(o);
13131         }
13132         if(index >= this.length){
13133             return this.add(key, o);
13134         }
13135         this.length++;
13136         this.items.splice(index, 0, o);
13137         if(typeof key != "undefined" && key != null){
13138             this.map[key] = o;
13139         }
13140         this.keys.splice(index, 0, key);
13141         this.fireEvent("add", index, o, key);
13142         return o;
13143     },
13144    
13145 /**
13146  * Removed an item from the collection.
13147  * @param {Object} o The item to remove.
13148  * @return {Object} The item removed.
13149  */
13150     remove : function(o){
13151         return this.removeAt(this.indexOf(o));
13152     },
13153    
13154 /**
13155  * Remove an item from a specified index in the collection.
13156  * @param {Number} index The index within the collection of the item to remove.
13157  */
13158     removeAt : function(index){
13159         if(index < this.length && index >= 0){
13160             this.length--;
13161             var o = this.items[index];
13162             this.items.splice(index, 1);
13163             var key = this.keys[index];
13164             if(typeof key != "undefined"){
13165                 delete this.map[key];
13166             }
13167             this.keys.splice(index, 1);
13168             this.fireEvent("remove", o, key);
13169         }
13170     },
13171    
13172 /**
13173  * Removed an item associated with the passed key fom the collection.
13174  * @param {String} key The key of the item to remove.
13175  */
13176     removeKey : function(key){
13177         return this.removeAt(this.indexOfKey(key));
13178     },
13179    
13180 /**
13181  * Returns the number of items in the collection.
13182  * @return {Number} the number of items in the collection.
13183  */
13184     getCount : function(){
13185         return this.length; 
13186     },
13187    
13188 /**
13189  * Returns index within the collection of the passed Object.
13190  * @param {Object} o The item to find the index of.
13191  * @return {Number} index of the item.
13192  */
13193     indexOf : function(o){
13194         if(!this.items.indexOf){
13195             for(var i = 0, len = this.items.length; i < len; i++){
13196                 if(this.items[i] == o) {
13197                     return i;
13198                 }
13199             }
13200             return -1;
13201         }else{
13202             return this.items.indexOf(o);
13203         }
13204     },
13205    
13206 /**
13207  * Returns index within the collection of the passed key.
13208  * @param {String} key The key to find the index of.
13209  * @return {Number} index of the key.
13210  */
13211     indexOfKey : function(key){
13212         if(!this.keys.indexOf){
13213             for(var i = 0, len = this.keys.length; i < len; i++){
13214                 if(this.keys[i] == key) {
13215                     return i;
13216                 }
13217             }
13218             return -1;
13219         }else{
13220             return this.keys.indexOf(key);
13221         }
13222     },
13223    
13224 /**
13225  * Returns the item associated with the passed key OR index. Key has priority over index.
13226  * @param {String/Number} key The key or index of the item.
13227  * @return {Object} The item associated with the passed key.
13228  */
13229     item : function(key){
13230         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13231         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13232     },
13233     
13234 /**
13235  * Returns the item at the specified index.
13236  * @param {Number} index The index of the item.
13237  * @return {Object}
13238  */
13239     itemAt : function(index){
13240         return this.items[index];
13241     },
13242     
13243 /**
13244  * Returns the item associated with the passed key.
13245  * @param {String/Number} key The key of the item.
13246  * @return {Object} The item associated with the passed key.
13247  */
13248     key : function(key){
13249         return this.map[key];
13250     },
13251    
13252 /**
13253  * Returns true if the collection contains the passed Object as an item.
13254  * @param {Object} o  The Object to look for in the collection.
13255  * @return {Boolean} True if the collection contains the Object as an item.
13256  */
13257     contains : function(o){
13258         return this.indexOf(o) != -1;
13259     },
13260    
13261 /**
13262  * Returns true if the collection contains the passed Object as a key.
13263  * @param {String} key The key to look for in the collection.
13264  * @return {Boolean} True if the collection contains the Object as a key.
13265  */
13266     containsKey : function(key){
13267         return typeof this.map[key] != "undefined";
13268     },
13269    
13270 /**
13271  * Removes all items from the collection.
13272  */
13273     clear : function(){
13274         this.length = 0;
13275         this.items = [];
13276         this.keys = [];
13277         this.map = {};
13278         this.fireEvent("clear");
13279     },
13280    
13281 /**
13282  * Returns the first item in the collection.
13283  * @return {Object} the first item in the collection..
13284  */
13285     first : function(){
13286         return this.items[0]; 
13287     },
13288    
13289 /**
13290  * Returns the last item in the collection.
13291  * @return {Object} the last item in the collection..
13292  */
13293     last : function(){
13294         return this.items[this.length-1];   
13295     },
13296     
13297     _sort : function(property, dir, fn){
13298         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13299         fn = fn || function(a, b){
13300             return a-b;
13301         };
13302         var c = [], k = this.keys, items = this.items;
13303         for(var i = 0, len = items.length; i < len; i++){
13304             c[c.length] = {key: k[i], value: items[i], index: i};
13305         }
13306         c.sort(function(a, b){
13307             var v = fn(a[property], b[property]) * dsc;
13308             if(v == 0){
13309                 v = (a.index < b.index ? -1 : 1);
13310             }
13311             return v;
13312         });
13313         for(var i = 0, len = c.length; i < len; i++){
13314             items[i] = c[i].value;
13315             k[i] = c[i].key;
13316         }
13317         this.fireEvent("sort", this);
13318     },
13319     
13320     /**
13321      * Sorts this collection with the passed comparison function
13322      * @param {String} direction (optional) "ASC" or "DESC"
13323      * @param {Function} fn (optional) comparison function
13324      */
13325     sort : function(dir, fn){
13326         this._sort("value", dir, fn);
13327     },
13328     
13329     /**
13330      * Sorts this collection by keys
13331      * @param {String} direction (optional) "ASC" or "DESC"
13332      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13333      */
13334     keySort : function(dir, fn){
13335         this._sort("key", dir, fn || function(a, b){
13336             return String(a).toUpperCase()-String(b).toUpperCase();
13337         });
13338     },
13339     
13340     /**
13341      * Returns a range of items in this collection
13342      * @param {Number} startIndex (optional) defaults to 0
13343      * @param {Number} endIndex (optional) default to the last item
13344      * @return {Array} An array of items
13345      */
13346     getRange : function(start, end){
13347         var items = this.items;
13348         if(items.length < 1){
13349             return [];
13350         }
13351         start = start || 0;
13352         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13353         var r = [];
13354         if(start <= end){
13355             for(var i = start; i <= end; i++) {
13356                     r[r.length] = items[i];
13357             }
13358         }else{
13359             for(var i = start; i >= end; i--) {
13360                     r[r.length] = items[i];
13361             }
13362         }
13363         return r;
13364     },
13365         
13366     /**
13367      * Filter the <i>objects</i> in this collection by a specific property. 
13368      * Returns a new collection that has been filtered.
13369      * @param {String} property A property on your objects
13370      * @param {String/RegExp} value Either string that the property values 
13371      * should start with or a RegExp to test against the property
13372      * @return {MixedCollection} The new filtered collection
13373      */
13374     filter : function(property, value){
13375         if(!value.exec){ // not a regex
13376             value = String(value);
13377             if(value.length == 0){
13378                 return this.clone();
13379             }
13380             value = new RegExp("^" + Roo.escapeRe(value), "i");
13381         }
13382         return this.filterBy(function(o){
13383             return o && value.test(o[property]);
13384         });
13385         },
13386     
13387     /**
13388      * Filter by a function. * Returns a new collection that has been filtered.
13389      * The passed function will be called with each 
13390      * object in the collection. If the function returns true, the value is included 
13391      * otherwise it is filtered.
13392      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13393      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13394      * @return {MixedCollection} The new filtered collection
13395      */
13396     filterBy : function(fn, scope){
13397         var r = new Roo.util.MixedCollection();
13398         r.getKey = this.getKey;
13399         var k = this.keys, it = this.items;
13400         for(var i = 0, len = it.length; i < len; i++){
13401             if(fn.call(scope||this, it[i], k[i])){
13402                                 r.add(k[i], it[i]);
13403                         }
13404         }
13405         return r;
13406     },
13407     
13408     /**
13409      * Creates a duplicate of this collection
13410      * @return {MixedCollection}
13411      */
13412     clone : function(){
13413         var r = new Roo.util.MixedCollection();
13414         var k = this.keys, it = this.items;
13415         for(var i = 0, len = it.length; i < len; i++){
13416             r.add(k[i], it[i]);
13417         }
13418         r.getKey = this.getKey;
13419         return r;
13420     }
13421 });
13422 /**
13423  * Returns the item associated with the passed key or index.
13424  * @method
13425  * @param {String/Number} key The key or index of the item.
13426  * @return {Object} The item associated with the passed key.
13427  */
13428 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13429  * Based on:
13430  * Ext JS Library 1.1.1
13431  * Copyright(c) 2006-2007, Ext JS, LLC.
13432  *
13433  * Originally Released Under LGPL - original licence link has changed is not relivant.
13434  *
13435  * Fork - LGPL
13436  * <script type="text/javascript">
13437  */
13438 /**
13439  * @class Roo.util.JSON
13440  * Modified version of Douglas Crockford"s json.js that doesn"t
13441  * mess with the Object prototype 
13442  * http://www.json.org/js.html
13443  * @singleton
13444  */
13445 Roo.util.JSON = new (function(){
13446     var useHasOwn = {}.hasOwnProperty ? true : false;
13447     
13448     // crashes Safari in some instances
13449     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13450     
13451     var pad = function(n) {
13452         return n < 10 ? "0" + n : n;
13453     };
13454     
13455     var m = {
13456         "\b": '\\b',
13457         "\t": '\\t',
13458         "\n": '\\n',
13459         "\f": '\\f',
13460         "\r": '\\r',
13461         '"' : '\\"',
13462         "\\": '\\\\'
13463     };
13464
13465     var encodeString = function(s){
13466         if (/["\\\x00-\x1f]/.test(s)) {
13467             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13468                 var c = m[b];
13469                 if(c){
13470                     return c;
13471                 }
13472                 c = b.charCodeAt();
13473                 return "\\u00" +
13474                     Math.floor(c / 16).toString(16) +
13475                     (c % 16).toString(16);
13476             }) + '"';
13477         }
13478         return '"' + s + '"';
13479     };
13480     
13481     var encodeArray = function(o){
13482         var a = ["["], b, i, l = o.length, v;
13483             for (i = 0; i < l; i += 1) {
13484                 v = o[i];
13485                 switch (typeof v) {
13486                     case "undefined":
13487                     case "function":
13488                     case "unknown":
13489                         break;
13490                     default:
13491                         if (b) {
13492                             a.push(',');
13493                         }
13494                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13495                         b = true;
13496                 }
13497             }
13498             a.push("]");
13499             return a.join("");
13500     };
13501     
13502     var encodeDate = function(o){
13503         return '"' + o.getFullYear() + "-" +
13504                 pad(o.getMonth() + 1) + "-" +
13505                 pad(o.getDate()) + "T" +
13506                 pad(o.getHours()) + ":" +
13507                 pad(o.getMinutes()) + ":" +
13508                 pad(o.getSeconds()) + '"';
13509     };
13510     
13511     /**
13512      * Encodes an Object, Array or other value
13513      * @param {Mixed} o The variable to encode
13514      * @return {String} The JSON string
13515      */
13516     this.encode = function(o)
13517     {
13518         // should this be extended to fully wrap stringify..
13519         
13520         if(typeof o == "undefined" || o === null){
13521             return "null";
13522         }else if(o instanceof Array){
13523             return encodeArray(o);
13524         }else if(o instanceof Date){
13525             return encodeDate(o);
13526         }else if(typeof o == "string"){
13527             return encodeString(o);
13528         }else if(typeof o == "number"){
13529             return isFinite(o) ? String(o) : "null";
13530         }else if(typeof o == "boolean"){
13531             return String(o);
13532         }else {
13533             var a = ["{"], b, i, v;
13534             for (i in o) {
13535                 if(!useHasOwn || o.hasOwnProperty(i)) {
13536                     v = o[i];
13537                     switch (typeof v) {
13538                     case "undefined":
13539                     case "function":
13540                     case "unknown":
13541                         break;
13542                     default:
13543                         if(b){
13544                             a.push(',');
13545                         }
13546                         a.push(this.encode(i), ":",
13547                                 v === null ? "null" : this.encode(v));
13548                         b = true;
13549                     }
13550                 }
13551             }
13552             a.push("}");
13553             return a.join("");
13554         }
13555     };
13556     
13557     /**
13558      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13559      * @param {String} json The JSON string
13560      * @return {Object} The resulting object
13561      */
13562     this.decode = function(json){
13563         
13564         return  /** eval:var:json */ eval("(" + json + ')');
13565     };
13566 })();
13567 /** 
13568  * Shorthand for {@link Roo.util.JSON#encode}
13569  * @member Roo encode 
13570  * @method */
13571 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13572 /** 
13573  * Shorthand for {@link Roo.util.JSON#decode}
13574  * @member Roo decode 
13575  * @method */
13576 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13577 /*
13578  * Based on:
13579  * Ext JS Library 1.1.1
13580  * Copyright(c) 2006-2007, Ext JS, LLC.
13581  *
13582  * Originally Released Under LGPL - original licence link has changed is not relivant.
13583  *
13584  * Fork - LGPL
13585  * <script type="text/javascript">
13586  */
13587  
13588 /**
13589  * @class Roo.util.Format
13590  * Reusable data formatting functions
13591  * @singleton
13592  */
13593 Roo.util.Format = function(){
13594     var trimRe = /^\s+|\s+$/g;
13595     return {
13596         /**
13597          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13598          * @param {String} value The string to truncate
13599          * @param {Number} length The maximum length to allow before truncating
13600          * @return {String} The converted text
13601          */
13602         ellipsis : function(value, len){
13603             if(value && value.length > len){
13604                 return value.substr(0, len-3)+"...";
13605             }
13606             return value;
13607         },
13608
13609         /**
13610          * Checks a reference and converts it to empty string if it is undefined
13611          * @param {Mixed} value Reference to check
13612          * @return {Mixed} Empty string if converted, otherwise the original value
13613          */
13614         undef : function(value){
13615             return typeof value != "undefined" ? value : "";
13616         },
13617
13618         /**
13619          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13620          * @param {String} value The string to encode
13621          * @return {String} The encoded text
13622          */
13623         htmlEncode : function(value){
13624             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13625         },
13626
13627         /**
13628          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13629          * @param {String} value The string to decode
13630          * @return {String} The decoded text
13631          */
13632         htmlDecode : function(value){
13633             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13634         },
13635
13636         /**
13637          * Trims any whitespace from either side of a string
13638          * @param {String} value The text to trim
13639          * @return {String} The trimmed text
13640          */
13641         trim : function(value){
13642             return String(value).replace(trimRe, "");
13643         },
13644
13645         /**
13646          * Returns a substring from within an original string
13647          * @param {String} value The original text
13648          * @param {Number} start The start index of the substring
13649          * @param {Number} length The length of the substring
13650          * @return {String} The substring
13651          */
13652         substr : function(value, start, length){
13653             return String(value).substr(start, length);
13654         },
13655
13656         /**
13657          * Converts a string to all lower case letters
13658          * @param {String} value The text to convert
13659          * @return {String} The converted text
13660          */
13661         lowercase : function(value){
13662             return String(value).toLowerCase();
13663         },
13664
13665         /**
13666          * Converts a string to all upper case letters
13667          * @param {String} value The text to convert
13668          * @return {String} The converted text
13669          */
13670         uppercase : function(value){
13671             return String(value).toUpperCase();
13672         },
13673
13674         /**
13675          * Converts the first character only of a string to upper case
13676          * @param {String} value The text to convert
13677          * @return {String} The converted text
13678          */
13679         capitalize : function(value){
13680             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13681         },
13682
13683         // private
13684         call : function(value, fn){
13685             if(arguments.length > 2){
13686                 var args = Array.prototype.slice.call(arguments, 2);
13687                 args.unshift(value);
13688                  
13689                 return /** eval:var:value */  eval(fn).apply(window, args);
13690             }else{
13691                 /** eval:var:value */
13692                 return /** eval:var:value */ eval(fn).call(window, value);
13693             }
13694         },
13695
13696        
13697         /**
13698          * safer version of Math.toFixed..??/
13699          * @param {Number/String} value The numeric value to format
13700          * @param {Number/String} value Decimal places 
13701          * @return {String} The formatted currency string
13702          */
13703         toFixed : function(v, n)
13704         {
13705             // why not use to fixed - precision is buggered???
13706             if (!n) {
13707                 return Math.round(v-0);
13708             }
13709             var fact = Math.pow(10,n+1);
13710             v = (Math.round((v-0)*fact))/fact;
13711             var z = (''+fact).substring(2);
13712             if (v == Math.floor(v)) {
13713                 return Math.floor(v) + '.' + z;
13714             }
13715             
13716             // now just padd decimals..
13717             var ps = String(v).split('.');
13718             var fd = (ps[1] + z);
13719             var r = fd.substring(0,n); 
13720             var rm = fd.substring(n); 
13721             if (rm < 5) {
13722                 return ps[0] + '.' + r;
13723             }
13724             r*=1; // turn it into a number;
13725             r++;
13726             if (String(r).length != n) {
13727                 ps[0]*=1;
13728                 ps[0]++;
13729                 r = String(r).substring(1); // chop the end off.
13730             }
13731             
13732             return ps[0] + '.' + r;
13733              
13734         },
13735         
13736         /**
13737          * Format a number as US currency
13738          * @param {Number/String} value The numeric value to format
13739          * @return {String} The formatted currency string
13740          */
13741         usMoney : function(v){
13742             return '$' + Roo.util.Format.number(v);
13743         },
13744         
13745         /**
13746          * Format a number
13747          * eventually this should probably emulate php's number_format
13748          * @param {Number/String} value The numeric value to format
13749          * @param {Number} decimals number of decimal places
13750          * @return {String} The formatted currency string
13751          */
13752         number : function(v,decimals)
13753         {
13754             // multiply and round.
13755             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13756             var mul = Math.pow(10, decimals);
13757             var zero = String(mul).substring(1);
13758             v = (Math.round((v-0)*mul))/mul;
13759             
13760             // if it's '0' number.. then
13761             
13762             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13763             v = String(v);
13764             var ps = v.split('.');
13765             var whole = ps[0];
13766             
13767             
13768             var r = /(\d+)(\d{3})/;
13769             // add comma's
13770             while (r.test(whole)) {
13771                 whole = whole.replace(r, '$1' + ',' + '$2');
13772             }
13773             
13774             
13775             var sub = ps[1] ?
13776                     // has decimals..
13777                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13778                     // does not have decimals
13779                     (decimals ? ('.' + zero) : '');
13780             
13781             
13782             return whole + sub ;
13783         },
13784         
13785         /**
13786          * Parse a value into a formatted date using the specified format pattern.
13787          * @param {Mixed} value The value to format
13788          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13789          * @return {String} The formatted date string
13790          */
13791         date : function(v, format){
13792             if(!v){
13793                 return "";
13794             }
13795             if(!(v instanceof Date)){
13796                 v = new Date(Date.parse(v));
13797             }
13798             return v.dateFormat(format || Roo.util.Format.defaults.date);
13799         },
13800
13801         /**
13802          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13803          * @param {String} format Any valid date format string
13804          * @return {Function} The date formatting function
13805          */
13806         dateRenderer : function(format){
13807             return function(v){
13808                 return Roo.util.Format.date(v, format);  
13809             };
13810         },
13811
13812         // private
13813         stripTagsRE : /<\/?[^>]+>/gi,
13814         
13815         /**
13816          * Strips all HTML tags
13817          * @param {Mixed} value The text from which to strip tags
13818          * @return {String} The stripped text
13819          */
13820         stripTags : function(v){
13821             return !v ? v : String(v).replace(this.stripTagsRE, "");
13822         }
13823     };
13824 }();
13825 Roo.util.Format.defaults = {
13826     date : 'd/M/Y'
13827 };/*
13828  * Based on:
13829  * Ext JS Library 1.1.1
13830  * Copyright(c) 2006-2007, Ext JS, LLC.
13831  *
13832  * Originally Released Under LGPL - original licence link has changed is not relivant.
13833  *
13834  * Fork - LGPL
13835  * <script type="text/javascript">
13836  */
13837
13838
13839  
13840
13841 /**
13842  * @class Roo.MasterTemplate
13843  * @extends Roo.Template
13844  * Provides a template that can have child templates. The syntax is:
13845 <pre><code>
13846 var t = new Roo.MasterTemplate(
13847         '&lt;select name="{name}"&gt;',
13848                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13849         '&lt;/select&gt;'
13850 );
13851 t.add('options', {value: 'foo', text: 'bar'});
13852 // or you can add multiple child elements in one shot
13853 t.addAll('options', [
13854     {value: 'foo', text: 'bar'},
13855     {value: 'foo2', text: 'bar2'},
13856     {value: 'foo3', text: 'bar3'}
13857 ]);
13858 // then append, applying the master template values
13859 t.append('my-form', {name: 'my-select'});
13860 </code></pre>
13861 * A name attribute for the child template is not required if you have only one child
13862 * template or you want to refer to them by index.
13863  */
13864 Roo.MasterTemplate = function(){
13865     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13866     this.originalHtml = this.html;
13867     var st = {};
13868     var m, re = this.subTemplateRe;
13869     re.lastIndex = 0;
13870     var subIndex = 0;
13871     while(m = re.exec(this.html)){
13872         var name = m[1], content = m[2];
13873         st[subIndex] = {
13874             name: name,
13875             index: subIndex,
13876             buffer: [],
13877             tpl : new Roo.Template(content)
13878         };
13879         if(name){
13880             st[name] = st[subIndex];
13881         }
13882         st[subIndex].tpl.compile();
13883         st[subIndex].tpl.call = this.call.createDelegate(this);
13884         subIndex++;
13885     }
13886     this.subCount = subIndex;
13887     this.subs = st;
13888 };
13889 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13890     /**
13891     * The regular expression used to match sub templates
13892     * @type RegExp
13893     * @property
13894     */
13895     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13896
13897     /**
13898      * Applies the passed values to a child template.
13899      * @param {String/Number} name (optional) The name or index of the child template
13900      * @param {Array/Object} values The values to be applied to the template
13901      * @return {MasterTemplate} this
13902      */
13903      add : function(name, values){
13904         if(arguments.length == 1){
13905             values = arguments[0];
13906             name = 0;
13907         }
13908         var s = this.subs[name];
13909         s.buffer[s.buffer.length] = s.tpl.apply(values);
13910         return this;
13911     },
13912
13913     /**
13914      * Applies all the passed values to a child template.
13915      * @param {String/Number} name (optional) The name or index of the child template
13916      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13917      * @param {Boolean} reset (optional) True to reset the template first
13918      * @return {MasterTemplate} this
13919      */
13920     fill : function(name, values, reset){
13921         var a = arguments;
13922         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13923             values = a[0];
13924             name = 0;
13925             reset = a[1];
13926         }
13927         if(reset){
13928             this.reset();
13929         }
13930         for(var i = 0, len = values.length; i < len; i++){
13931             this.add(name, values[i]);
13932         }
13933         return this;
13934     },
13935
13936     /**
13937      * Resets the template for reuse
13938      * @return {MasterTemplate} this
13939      */
13940      reset : function(){
13941         var s = this.subs;
13942         for(var i = 0; i < this.subCount; i++){
13943             s[i].buffer = [];
13944         }
13945         return this;
13946     },
13947
13948     applyTemplate : function(values){
13949         var s = this.subs;
13950         var replaceIndex = -1;
13951         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13952             return s[++replaceIndex].buffer.join("");
13953         });
13954         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13955     },
13956
13957     apply : function(){
13958         return this.applyTemplate.apply(this, arguments);
13959     },
13960
13961     compile : function(){return this;}
13962 });
13963
13964 /**
13965  * Alias for fill().
13966  * @method
13967  */
13968 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13969  /**
13970  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13971  * var tpl = Roo.MasterTemplate.from('element-id');
13972  * @param {String/HTMLElement} el
13973  * @param {Object} config
13974  * @static
13975  */
13976 Roo.MasterTemplate.from = function(el, config){
13977     el = Roo.getDom(el);
13978     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13979 };/*
13980  * Based on:
13981  * Ext JS Library 1.1.1
13982  * Copyright(c) 2006-2007, Ext JS, LLC.
13983  *
13984  * Originally Released Under LGPL - original licence link has changed is not relivant.
13985  *
13986  * Fork - LGPL
13987  * <script type="text/javascript">
13988  */
13989
13990  
13991 /**
13992  * @class Roo.util.CSS
13993  * Utility class for manipulating CSS rules
13994  * @singleton
13995  */
13996 Roo.util.CSS = function(){
13997         var rules = null;
13998         var doc = document;
13999
14000     var camelRe = /(-[a-z])/gi;
14001     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14002
14003    return {
14004    /**
14005     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14006     * tag and appended to the HEAD of the document.
14007     * @param {String|Object} cssText The text containing the css rules
14008     * @param {String} id An id to add to the stylesheet for later removal
14009     * @return {StyleSheet}
14010     */
14011     createStyleSheet : function(cssText, id){
14012         var ss;
14013         var head = doc.getElementsByTagName("head")[0];
14014         var nrules = doc.createElement("style");
14015         nrules.setAttribute("type", "text/css");
14016         if(id){
14017             nrules.setAttribute("id", id);
14018         }
14019         if (typeof(cssText) != 'string') {
14020             // support object maps..
14021             // not sure if this a good idea.. 
14022             // perhaps it should be merged with the general css handling
14023             // and handle js style props.
14024             var cssTextNew = [];
14025             for(var n in cssText) {
14026                 var citems = [];
14027                 for(var k in cssText[n]) {
14028                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14029                 }
14030                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14031                 
14032             }
14033             cssText = cssTextNew.join("\n");
14034             
14035         }
14036        
14037        
14038        if(Roo.isIE){
14039            head.appendChild(nrules);
14040            ss = nrules.styleSheet;
14041            ss.cssText = cssText;
14042        }else{
14043            try{
14044                 nrules.appendChild(doc.createTextNode(cssText));
14045            }catch(e){
14046                nrules.cssText = cssText; 
14047            }
14048            head.appendChild(nrules);
14049            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14050        }
14051        this.cacheStyleSheet(ss);
14052        return ss;
14053    },
14054
14055    /**
14056     * Removes a style or link tag by id
14057     * @param {String} id The id of the tag
14058     */
14059    removeStyleSheet : function(id){
14060        var existing = doc.getElementById(id);
14061        if(existing){
14062            existing.parentNode.removeChild(existing);
14063        }
14064    },
14065
14066    /**
14067     * Dynamically swaps an existing stylesheet reference for a new one
14068     * @param {String} id The id of an existing link tag to remove
14069     * @param {String} url The href of the new stylesheet to include
14070     */
14071    swapStyleSheet : function(id, url){
14072        this.removeStyleSheet(id);
14073        var ss = doc.createElement("link");
14074        ss.setAttribute("rel", "stylesheet");
14075        ss.setAttribute("type", "text/css");
14076        ss.setAttribute("id", id);
14077        ss.setAttribute("href", url);
14078        doc.getElementsByTagName("head")[0].appendChild(ss);
14079    },
14080    
14081    /**
14082     * Refresh the rule cache if you have dynamically added stylesheets
14083     * @return {Object} An object (hash) of rules indexed by selector
14084     */
14085    refreshCache : function(){
14086        return this.getRules(true);
14087    },
14088
14089    // private
14090    cacheStyleSheet : function(stylesheet){
14091        if(!rules){
14092            rules = {};
14093        }
14094        try{// try catch for cross domain access issue
14095            var ssRules = stylesheet.cssRules || stylesheet.rules;
14096            for(var j = ssRules.length-1; j >= 0; --j){
14097                rules[ssRules[j].selectorText] = ssRules[j];
14098            }
14099        }catch(e){}
14100    },
14101    
14102    /**
14103     * Gets all css rules for the document
14104     * @param {Boolean} refreshCache true to refresh the internal cache
14105     * @return {Object} An object (hash) of rules indexed by selector
14106     */
14107    getRules : function(refreshCache){
14108                 if(rules == null || refreshCache){
14109                         rules = {};
14110                         var ds = doc.styleSheets;
14111                         for(var i =0, len = ds.length; i < len; i++){
14112                             try{
14113                         this.cacheStyleSheet(ds[i]);
14114                     }catch(e){} 
14115                 }
14116                 }
14117                 return rules;
14118         },
14119         
14120         /**
14121     * Gets an an individual CSS rule by selector(s)
14122     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14123     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14124     * @return {CSSRule} The CSS rule or null if one is not found
14125     */
14126    getRule : function(selector, refreshCache){
14127                 var rs = this.getRules(refreshCache);
14128                 if(!(selector instanceof Array)){
14129                     return rs[selector];
14130                 }
14131                 for(var i = 0; i < selector.length; i++){
14132                         if(rs[selector[i]]){
14133                                 return rs[selector[i]];
14134                         }
14135                 }
14136                 return null;
14137         },
14138         
14139         
14140         /**
14141     * Updates a rule property
14142     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14143     * @param {String} property The css property
14144     * @param {String} value The new value for the property
14145     * @return {Boolean} true If a rule was found and updated
14146     */
14147    updateRule : function(selector, property, value){
14148                 if(!(selector instanceof Array)){
14149                         var rule = this.getRule(selector);
14150                         if(rule){
14151                                 rule.style[property.replace(camelRe, camelFn)] = value;
14152                                 return true;
14153                         }
14154                 }else{
14155                         for(var i = 0; i < selector.length; i++){
14156                                 if(this.updateRule(selector[i], property, value)){
14157                                         return true;
14158                                 }
14159                         }
14160                 }
14161                 return false;
14162         }
14163    };   
14164 }();/*
14165  * Based on:
14166  * Ext JS Library 1.1.1
14167  * Copyright(c) 2006-2007, Ext JS, LLC.
14168  *
14169  * Originally Released Under LGPL - original licence link has changed is not relivant.
14170  *
14171  * Fork - LGPL
14172  * <script type="text/javascript">
14173  */
14174
14175  
14176
14177 /**
14178  * @class Roo.util.ClickRepeater
14179  * @extends Roo.util.Observable
14180  * 
14181  * A wrapper class which can be applied to any element. Fires a "click" event while the
14182  * mouse is pressed. The interval between firings may be specified in the config but
14183  * defaults to 10 milliseconds.
14184  * 
14185  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14186  * 
14187  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14188  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14189  * Similar to an autorepeat key delay.
14190  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14191  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14192  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14193  *           "interval" and "delay" are ignored. "immediate" is honored.
14194  * @cfg {Boolean} preventDefault True to prevent the default click event
14195  * @cfg {Boolean} stopDefault True to stop the default click event
14196  * 
14197  * @history
14198  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14199  *     2007-02-02 jvs Renamed to ClickRepeater
14200  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14201  *
14202  *  @constructor
14203  * @param {String/HTMLElement/Element} el The element to listen on
14204  * @param {Object} config
14205  **/
14206 Roo.util.ClickRepeater = function(el, config)
14207 {
14208     this.el = Roo.get(el);
14209     this.el.unselectable();
14210
14211     Roo.apply(this, config);
14212
14213     this.addEvents({
14214     /**
14215      * @event mousedown
14216      * Fires when the mouse button is depressed.
14217      * @param {Roo.util.ClickRepeater} this
14218      */
14219         "mousedown" : true,
14220     /**
14221      * @event click
14222      * Fires on a specified interval during the time the element is pressed.
14223      * @param {Roo.util.ClickRepeater} this
14224      */
14225         "click" : true,
14226     /**
14227      * @event mouseup
14228      * Fires when the mouse key is released.
14229      * @param {Roo.util.ClickRepeater} this
14230      */
14231         "mouseup" : true
14232     });
14233
14234     this.el.on("mousedown", this.handleMouseDown, this);
14235     if(this.preventDefault || this.stopDefault){
14236         this.el.on("click", function(e){
14237             if(this.preventDefault){
14238                 e.preventDefault();
14239             }
14240             if(this.stopDefault){
14241                 e.stopEvent();
14242             }
14243         }, this);
14244     }
14245
14246     // allow inline handler
14247     if(this.handler){
14248         this.on("click", this.handler,  this.scope || this);
14249     }
14250
14251     Roo.util.ClickRepeater.superclass.constructor.call(this);
14252 };
14253
14254 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14255     interval : 20,
14256     delay: 250,
14257     preventDefault : true,
14258     stopDefault : false,
14259     timer : 0,
14260
14261     // private
14262     handleMouseDown : function(){
14263         clearTimeout(this.timer);
14264         this.el.blur();
14265         if(this.pressClass){
14266             this.el.addClass(this.pressClass);
14267         }
14268         this.mousedownTime = new Date();
14269
14270         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14271         this.el.on("mouseout", this.handleMouseOut, this);
14272
14273         this.fireEvent("mousedown", this);
14274         this.fireEvent("click", this);
14275         
14276         this.timer = this.click.defer(this.delay || this.interval, this);
14277     },
14278
14279     // private
14280     click : function(){
14281         this.fireEvent("click", this);
14282         this.timer = this.click.defer(this.getInterval(), this);
14283     },
14284
14285     // private
14286     getInterval: function(){
14287         if(!this.accelerate){
14288             return this.interval;
14289         }
14290         var pressTime = this.mousedownTime.getElapsed();
14291         if(pressTime < 500){
14292             return 400;
14293         }else if(pressTime < 1700){
14294             return 320;
14295         }else if(pressTime < 2600){
14296             return 250;
14297         }else if(pressTime < 3500){
14298             return 180;
14299         }else if(pressTime < 4400){
14300             return 140;
14301         }else if(pressTime < 5300){
14302             return 80;
14303         }else if(pressTime < 6200){
14304             return 50;
14305         }else{
14306             return 10;
14307         }
14308     },
14309
14310     // private
14311     handleMouseOut : function(){
14312         clearTimeout(this.timer);
14313         if(this.pressClass){
14314             this.el.removeClass(this.pressClass);
14315         }
14316         this.el.on("mouseover", this.handleMouseReturn, this);
14317     },
14318
14319     // private
14320     handleMouseReturn : function(){
14321         this.el.un("mouseover", this.handleMouseReturn);
14322         if(this.pressClass){
14323             this.el.addClass(this.pressClass);
14324         }
14325         this.click();
14326     },
14327
14328     // private
14329     handleMouseUp : function(){
14330         clearTimeout(this.timer);
14331         this.el.un("mouseover", this.handleMouseReturn);
14332         this.el.un("mouseout", this.handleMouseOut);
14333         Roo.get(document).un("mouseup", this.handleMouseUp);
14334         this.el.removeClass(this.pressClass);
14335         this.fireEvent("mouseup", this);
14336     }
14337 });/*
14338  * Based on:
14339  * Ext JS Library 1.1.1
14340  * Copyright(c) 2006-2007, Ext JS, LLC.
14341  *
14342  * Originally Released Under LGPL - original licence link has changed is not relivant.
14343  *
14344  * Fork - LGPL
14345  * <script type="text/javascript">
14346  */
14347
14348  
14349 /**
14350  * @class Roo.KeyNav
14351  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14352  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14353  * way to implement custom navigation schemes for any UI component.</p>
14354  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14355  * pageUp, pageDown, del, home, end.  Usage:</p>
14356  <pre><code>
14357 var nav = new Roo.KeyNav("my-element", {
14358     "left" : function(e){
14359         this.moveLeft(e.ctrlKey);
14360     },
14361     "right" : function(e){
14362         this.moveRight(e.ctrlKey);
14363     },
14364     "enter" : function(e){
14365         this.save();
14366     },
14367     scope : this
14368 });
14369 </code></pre>
14370  * @constructor
14371  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14372  * @param {Object} config The config
14373  */
14374 Roo.KeyNav = function(el, config){
14375     this.el = Roo.get(el);
14376     Roo.apply(this, config);
14377     if(!this.disabled){
14378         this.disabled = true;
14379         this.enable();
14380     }
14381 };
14382
14383 Roo.KeyNav.prototype = {
14384     /**
14385      * @cfg {Boolean} disabled
14386      * True to disable this KeyNav instance (defaults to false)
14387      */
14388     disabled : false,
14389     /**
14390      * @cfg {String} defaultEventAction
14391      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14392      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14393      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14394      */
14395     defaultEventAction: "stopEvent",
14396     /**
14397      * @cfg {Boolean} forceKeyDown
14398      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14399      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14400      * handle keydown instead of keypress.
14401      */
14402     forceKeyDown : false,
14403
14404     // private
14405     prepareEvent : function(e){
14406         var k = e.getKey();
14407         var h = this.keyToHandler[k];
14408         //if(h && this[h]){
14409         //    e.stopPropagation();
14410         //}
14411         if(Roo.isSafari && h && k >= 37 && k <= 40){
14412             e.stopEvent();
14413         }
14414     },
14415
14416     // private
14417     relay : function(e){
14418         var k = e.getKey();
14419         var h = this.keyToHandler[k];
14420         if(h && this[h]){
14421             if(this.doRelay(e, this[h], h) !== true){
14422                 e[this.defaultEventAction]();
14423             }
14424         }
14425     },
14426
14427     // private
14428     doRelay : function(e, h, hname){
14429         return h.call(this.scope || this, e);
14430     },
14431
14432     // possible handlers
14433     enter : false,
14434     left : false,
14435     right : false,
14436     up : false,
14437     down : false,
14438     tab : false,
14439     esc : false,
14440     pageUp : false,
14441     pageDown : false,
14442     del : false,
14443     home : false,
14444     end : false,
14445
14446     // quick lookup hash
14447     keyToHandler : {
14448         37 : "left",
14449         39 : "right",
14450         38 : "up",
14451         40 : "down",
14452         33 : "pageUp",
14453         34 : "pageDown",
14454         46 : "del",
14455         36 : "home",
14456         35 : "end",
14457         13 : "enter",
14458         27 : "esc",
14459         9  : "tab"
14460     },
14461
14462         /**
14463          * Enable this KeyNav
14464          */
14465         enable: function(){
14466                 if(this.disabled){
14467             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14468             // the EventObject will normalize Safari automatically
14469             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14470                 this.el.on("keydown", this.relay,  this);
14471             }else{
14472                 this.el.on("keydown", this.prepareEvent,  this);
14473                 this.el.on("keypress", this.relay,  this);
14474             }
14475                     this.disabled = false;
14476                 }
14477         },
14478
14479         /**
14480          * Disable this KeyNav
14481          */
14482         disable: function(){
14483                 if(!this.disabled){
14484                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14485                 this.el.un("keydown", this.relay);
14486             }else{
14487                 this.el.un("keydown", this.prepareEvent);
14488                 this.el.un("keypress", this.relay);
14489             }
14490                     this.disabled = true;
14491                 }
14492         }
14493 };/*
14494  * Based on:
14495  * Ext JS Library 1.1.1
14496  * Copyright(c) 2006-2007, Ext JS, LLC.
14497  *
14498  * Originally Released Under LGPL - original licence link has changed is not relivant.
14499  *
14500  * Fork - LGPL
14501  * <script type="text/javascript">
14502  */
14503
14504  
14505 /**
14506  * @class Roo.KeyMap
14507  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14508  * The constructor accepts the same config object as defined by {@link #addBinding}.
14509  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14510  * combination it will call the function with this signature (if the match is a multi-key
14511  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14512  * A KeyMap can also handle a string representation of keys.<br />
14513  * Usage:
14514  <pre><code>
14515 // map one key by key code
14516 var map = new Roo.KeyMap("my-element", {
14517     key: 13, // or Roo.EventObject.ENTER
14518     fn: myHandler,
14519     scope: myObject
14520 });
14521
14522 // map multiple keys to one action by string
14523 var map = new Roo.KeyMap("my-element", {
14524     key: "a\r\n\t",
14525     fn: myHandler,
14526     scope: myObject
14527 });
14528
14529 // map multiple keys to multiple actions by strings and array of codes
14530 var map = new Roo.KeyMap("my-element", [
14531     {
14532         key: [10,13],
14533         fn: function(){ alert("Return was pressed"); }
14534     }, {
14535         key: "abc",
14536         fn: function(){ alert('a, b or c was pressed'); }
14537     }, {
14538         key: "\t",
14539         ctrl:true,
14540         shift:true,
14541         fn: function(){ alert('Control + shift + tab was pressed.'); }
14542     }
14543 ]);
14544 </code></pre>
14545  * <b>Note: A KeyMap starts enabled</b>
14546  * @constructor
14547  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14548  * @param {Object} config The config (see {@link #addBinding})
14549  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14550  */
14551 Roo.KeyMap = function(el, config, eventName){
14552     this.el  = Roo.get(el);
14553     this.eventName = eventName || "keydown";
14554     this.bindings = [];
14555     if(config){
14556         this.addBinding(config);
14557     }
14558     this.enable();
14559 };
14560
14561 Roo.KeyMap.prototype = {
14562     /**
14563      * True to stop the event from bubbling and prevent the default browser action if the
14564      * key was handled by the KeyMap (defaults to false)
14565      * @type Boolean
14566      */
14567     stopEvent : false,
14568
14569     /**
14570      * Add a new binding to this KeyMap. The following config object properties are supported:
14571      * <pre>
14572 Property    Type             Description
14573 ----------  ---------------  ----------------------------------------------------------------------
14574 key         String/Array     A single keycode or an array of keycodes to handle
14575 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14576 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14577 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14578 fn          Function         The function to call when KeyMap finds the expected key combination
14579 scope       Object           The scope of the callback function
14580 </pre>
14581      *
14582      * Usage:
14583      * <pre><code>
14584 // Create a KeyMap
14585 var map = new Roo.KeyMap(document, {
14586     key: Roo.EventObject.ENTER,
14587     fn: handleKey,
14588     scope: this
14589 });
14590
14591 //Add a new binding to the existing KeyMap later
14592 map.addBinding({
14593     key: 'abc',
14594     shift: true,
14595     fn: handleKey,
14596     scope: this
14597 });
14598 </code></pre>
14599      * @param {Object/Array} config A single KeyMap config or an array of configs
14600      */
14601         addBinding : function(config){
14602         if(config instanceof Array){
14603             for(var i = 0, len = config.length; i < len; i++){
14604                 this.addBinding(config[i]);
14605             }
14606             return;
14607         }
14608         var keyCode = config.key,
14609             shift = config.shift, 
14610             ctrl = config.ctrl, 
14611             alt = config.alt,
14612             fn = config.fn,
14613             scope = config.scope;
14614         if(typeof keyCode == "string"){
14615             var ks = [];
14616             var keyString = keyCode.toUpperCase();
14617             for(var j = 0, len = keyString.length; j < len; j++){
14618                 ks.push(keyString.charCodeAt(j));
14619             }
14620             keyCode = ks;
14621         }
14622         var keyArray = keyCode instanceof Array;
14623         var handler = function(e){
14624             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14625                 var k = e.getKey();
14626                 if(keyArray){
14627                     for(var i = 0, len = keyCode.length; i < len; i++){
14628                         if(keyCode[i] == k){
14629                           if(this.stopEvent){
14630                               e.stopEvent();
14631                           }
14632                           fn.call(scope || window, k, e);
14633                           return;
14634                         }
14635                     }
14636                 }else{
14637                     if(k == keyCode){
14638                         if(this.stopEvent){
14639                            e.stopEvent();
14640                         }
14641                         fn.call(scope || window, k, e);
14642                     }
14643                 }
14644             }
14645         };
14646         this.bindings.push(handler);  
14647         },
14648
14649     /**
14650      * Shorthand for adding a single key listener
14651      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14652      * following options:
14653      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14654      * @param {Function} fn The function to call
14655      * @param {Object} scope (optional) The scope of the function
14656      */
14657     on : function(key, fn, scope){
14658         var keyCode, shift, ctrl, alt;
14659         if(typeof key == "object" && !(key instanceof Array)){
14660             keyCode = key.key;
14661             shift = key.shift;
14662             ctrl = key.ctrl;
14663             alt = key.alt;
14664         }else{
14665             keyCode = key;
14666         }
14667         this.addBinding({
14668             key: keyCode,
14669             shift: shift,
14670             ctrl: ctrl,
14671             alt: alt,
14672             fn: fn,
14673             scope: scope
14674         })
14675     },
14676
14677     // private
14678     handleKeyDown : function(e){
14679             if(this.enabled){ //just in case
14680             var b = this.bindings;
14681             for(var i = 0, len = b.length; i < len; i++){
14682                 b[i].call(this, e);
14683             }
14684             }
14685         },
14686         
14687         /**
14688          * Returns true if this KeyMap is enabled
14689          * @return {Boolean} 
14690          */
14691         isEnabled : function(){
14692             return this.enabled;  
14693         },
14694         
14695         /**
14696          * Enables this KeyMap
14697          */
14698         enable: function(){
14699                 if(!this.enabled){
14700                     this.el.on(this.eventName, this.handleKeyDown, this);
14701                     this.enabled = true;
14702                 }
14703         },
14704
14705         /**
14706          * Disable this KeyMap
14707          */
14708         disable: function(){
14709                 if(this.enabled){
14710                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14711                     this.enabled = false;
14712                 }
14713         }
14714 };/*
14715  * Based on:
14716  * Ext JS Library 1.1.1
14717  * Copyright(c) 2006-2007, Ext JS, LLC.
14718  *
14719  * Originally Released Under LGPL - original licence link has changed is not relivant.
14720  *
14721  * Fork - LGPL
14722  * <script type="text/javascript">
14723  */
14724
14725  
14726 /**
14727  * @class Roo.util.TextMetrics
14728  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14729  * wide, in pixels, a given block of text will be.
14730  * @singleton
14731  */
14732 Roo.util.TextMetrics = function(){
14733     var shared;
14734     return {
14735         /**
14736          * Measures the size of the specified text
14737          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14738          * that can affect the size of the rendered text
14739          * @param {String} text The text to measure
14740          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14741          * in order to accurately measure the text height
14742          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14743          */
14744         measure : function(el, text, fixedWidth){
14745             if(!shared){
14746                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14747             }
14748             shared.bind(el);
14749             shared.setFixedWidth(fixedWidth || 'auto');
14750             return shared.getSize(text);
14751         },
14752
14753         /**
14754          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14755          * the overhead of multiple calls to initialize the style properties on each measurement.
14756          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14757          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14758          * in order to accurately measure the text height
14759          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14760          */
14761         createInstance : function(el, fixedWidth){
14762             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14763         }
14764     };
14765 }();
14766
14767  
14768
14769 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14770     var ml = new Roo.Element(document.createElement('div'));
14771     document.body.appendChild(ml.dom);
14772     ml.position('absolute');
14773     ml.setLeftTop(-1000, -1000);
14774     ml.hide();
14775
14776     if(fixedWidth){
14777         ml.setWidth(fixedWidth);
14778     }
14779      
14780     var instance = {
14781         /**
14782          * Returns the size of the specified text based on the internal element's style and width properties
14783          * @memberOf Roo.util.TextMetrics.Instance#
14784          * @param {String} text The text to measure
14785          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14786          */
14787         getSize : function(text){
14788             ml.update(text);
14789             var s = ml.getSize();
14790             ml.update('');
14791             return s;
14792         },
14793
14794         /**
14795          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14796          * that can affect the size of the rendered text
14797          * @memberOf Roo.util.TextMetrics.Instance#
14798          * @param {String/HTMLElement} el The element, dom node or id
14799          */
14800         bind : function(el){
14801             ml.setStyle(
14802                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14803             );
14804         },
14805
14806         /**
14807          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14808          * to set a fixed width in order to accurately measure the text height.
14809          * @memberOf Roo.util.TextMetrics.Instance#
14810          * @param {Number} width The width to set on the element
14811          */
14812         setFixedWidth : function(width){
14813             ml.setWidth(width);
14814         },
14815
14816         /**
14817          * Returns the measured width of the specified text
14818          * @memberOf Roo.util.TextMetrics.Instance#
14819          * @param {String} text The text to measure
14820          * @return {Number} width The width in pixels
14821          */
14822         getWidth : function(text){
14823             ml.dom.style.width = 'auto';
14824             return this.getSize(text).width;
14825         },
14826
14827         /**
14828          * Returns the measured height of the specified text.  For multiline text, be sure to call
14829          * {@link #setFixedWidth} if necessary.
14830          * @memberOf Roo.util.TextMetrics.Instance#
14831          * @param {String} text The text to measure
14832          * @return {Number} height The height in pixels
14833          */
14834         getHeight : function(text){
14835             return this.getSize(text).height;
14836         }
14837     };
14838
14839     instance.bind(bindTo);
14840
14841     return instance;
14842 };
14843
14844 // backwards compat
14845 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14846  * Based on:
14847  * Ext JS Library 1.1.1
14848  * Copyright(c) 2006-2007, Ext JS, LLC.
14849  *
14850  * Originally Released Under LGPL - original licence link has changed is not relivant.
14851  *
14852  * Fork - LGPL
14853  * <script type="text/javascript">
14854  */
14855
14856 /**
14857  * @class Roo.state.Provider
14858  * Abstract base class for state provider implementations. This class provides methods
14859  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14860  * Provider interface.
14861  */
14862 Roo.state.Provider = function(){
14863     /**
14864      * @event statechange
14865      * Fires when a state change occurs.
14866      * @param {Provider} this This state provider
14867      * @param {String} key The state key which was changed
14868      * @param {String} value The encoded value for the state
14869      */
14870     this.addEvents({
14871         "statechange": true
14872     });
14873     this.state = {};
14874     Roo.state.Provider.superclass.constructor.call(this);
14875 };
14876 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14877     /**
14878      * Returns the current value for a key
14879      * @param {String} name The key name
14880      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14881      * @return {Mixed} The state data
14882      */
14883     get : function(name, defaultValue){
14884         return typeof this.state[name] == "undefined" ?
14885             defaultValue : this.state[name];
14886     },
14887     
14888     /**
14889      * Clears a value from the state
14890      * @param {String} name The key name
14891      */
14892     clear : function(name){
14893         delete this.state[name];
14894         this.fireEvent("statechange", this, name, null);
14895     },
14896     
14897     /**
14898      * Sets the value for a key
14899      * @param {String} name The key name
14900      * @param {Mixed} value The value to set
14901      */
14902     set : function(name, value){
14903         this.state[name] = value;
14904         this.fireEvent("statechange", this, name, value);
14905     },
14906     
14907     /**
14908      * Decodes a string previously encoded with {@link #encodeValue}.
14909      * @param {String} value The value to decode
14910      * @return {Mixed} The decoded value
14911      */
14912     decodeValue : function(cookie){
14913         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14914         var matches = re.exec(unescape(cookie));
14915         if(!matches || !matches[1]) {
14916             return; // non state cookie
14917         }
14918         var type = matches[1];
14919         var v = matches[2];
14920         switch(type){
14921             case "n":
14922                 return parseFloat(v);
14923             case "d":
14924                 return new Date(Date.parse(v));
14925             case "b":
14926                 return (v == "1");
14927             case "a":
14928                 var all = [];
14929                 var values = v.split("^");
14930                 for(var i = 0, len = values.length; i < len; i++){
14931                     all.push(this.decodeValue(values[i]));
14932                 }
14933                 return all;
14934            case "o":
14935                 var all = {};
14936                 var values = v.split("^");
14937                 for(var i = 0, len = values.length; i < len; i++){
14938                     var kv = values[i].split("=");
14939                     all[kv[0]] = this.decodeValue(kv[1]);
14940                 }
14941                 return all;
14942            default:
14943                 return v;
14944         }
14945     },
14946     
14947     /**
14948      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14949      * @param {Mixed} value The value to encode
14950      * @return {String} The encoded value
14951      */
14952     encodeValue : function(v){
14953         var enc;
14954         if(typeof v == "number"){
14955             enc = "n:" + v;
14956         }else if(typeof v == "boolean"){
14957             enc = "b:" + (v ? "1" : "0");
14958         }else if(v instanceof Date){
14959             enc = "d:" + v.toGMTString();
14960         }else if(v instanceof Array){
14961             var flat = "";
14962             for(var i = 0, len = v.length; i < len; i++){
14963                 flat += this.encodeValue(v[i]);
14964                 if(i != len-1) {
14965                     flat += "^";
14966                 }
14967             }
14968             enc = "a:" + flat;
14969         }else if(typeof v == "object"){
14970             var flat = "";
14971             for(var key in v){
14972                 if(typeof v[key] != "function"){
14973                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14974                 }
14975             }
14976             enc = "o:" + flat.substring(0, flat.length-1);
14977         }else{
14978             enc = "s:" + v;
14979         }
14980         return escape(enc);        
14981     }
14982 });
14983
14984 /*
14985  * Based on:
14986  * Ext JS Library 1.1.1
14987  * Copyright(c) 2006-2007, Ext JS, LLC.
14988  *
14989  * Originally Released Under LGPL - original licence link has changed is not relivant.
14990  *
14991  * Fork - LGPL
14992  * <script type="text/javascript">
14993  */
14994 /**
14995  * @class Roo.state.Manager
14996  * This is the global state manager. By default all components that are "state aware" check this class
14997  * for state information if you don't pass them a custom state provider. In order for this class
14998  * to be useful, it must be initialized with a provider when your application initializes.
14999  <pre><code>
15000 // in your initialization function
15001 init : function(){
15002    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15003    ...
15004    // supposed you have a {@link Roo.BorderLayout}
15005    var layout = new Roo.BorderLayout(...);
15006    layout.restoreState();
15007    // or a {Roo.BasicDialog}
15008    var dialog = new Roo.BasicDialog(...);
15009    dialog.restoreState();
15010  </code></pre>
15011  * @singleton
15012  */
15013 Roo.state.Manager = function(){
15014     var provider = new Roo.state.Provider();
15015     
15016     return {
15017         /**
15018          * Configures the default state provider for your application
15019          * @param {Provider} stateProvider The state provider to set
15020          */
15021         setProvider : function(stateProvider){
15022             provider = stateProvider;
15023         },
15024         
15025         /**
15026          * Returns the current value for a key
15027          * @param {String} name The key name
15028          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15029          * @return {Mixed} The state data
15030          */
15031         get : function(key, defaultValue){
15032             return provider.get(key, defaultValue);
15033         },
15034         
15035         /**
15036          * Sets the value for a key
15037          * @param {String} name The key name
15038          * @param {Mixed} value The state data
15039          */
15040          set : function(key, value){
15041             provider.set(key, value);
15042         },
15043         
15044         /**
15045          * Clears a value from the state
15046          * @param {String} name The key name
15047          */
15048         clear : function(key){
15049             provider.clear(key);
15050         },
15051         
15052         /**
15053          * Gets the currently configured state provider
15054          * @return {Provider} The state provider
15055          */
15056         getProvider : function(){
15057             return provider;
15058         }
15059     };
15060 }();
15061 /*
15062  * Based on:
15063  * Ext JS Library 1.1.1
15064  * Copyright(c) 2006-2007, Ext JS, LLC.
15065  *
15066  * Originally Released Under LGPL - original licence link has changed is not relivant.
15067  *
15068  * Fork - LGPL
15069  * <script type="text/javascript">
15070  */
15071 /**
15072  * @class Roo.state.CookieProvider
15073  * @extends Roo.state.Provider
15074  * The default Provider implementation which saves state via cookies.
15075  * <br />Usage:
15076  <pre><code>
15077    var cp = new Roo.state.CookieProvider({
15078        path: "/cgi-bin/",
15079        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15080        domain: "roojs.com"
15081    })
15082    Roo.state.Manager.setProvider(cp);
15083  </code></pre>
15084  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15085  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15086  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15087  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15088  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15089  * domain the page is running on including the 'www' like 'www.roojs.com')
15090  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15091  * @constructor
15092  * Create a new CookieProvider
15093  * @param {Object} config The configuration object
15094  */
15095 Roo.state.CookieProvider = function(config){
15096     Roo.state.CookieProvider.superclass.constructor.call(this);
15097     this.path = "/";
15098     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15099     this.domain = null;
15100     this.secure = false;
15101     Roo.apply(this, config);
15102     this.state = this.readCookies();
15103 };
15104
15105 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15106     // private
15107     set : function(name, value){
15108         if(typeof value == "undefined" || value === null){
15109             this.clear(name);
15110             return;
15111         }
15112         this.setCookie(name, value);
15113         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15114     },
15115
15116     // private
15117     clear : function(name){
15118         this.clearCookie(name);
15119         Roo.state.CookieProvider.superclass.clear.call(this, name);
15120     },
15121
15122     // private
15123     readCookies : function(){
15124         var cookies = {};
15125         var c = document.cookie + ";";
15126         var re = /\s?(.*?)=(.*?);/g;
15127         var matches;
15128         while((matches = re.exec(c)) != null){
15129             var name = matches[1];
15130             var value = matches[2];
15131             if(name && name.substring(0,3) == "ys-"){
15132                 cookies[name.substr(3)] = this.decodeValue(value);
15133             }
15134         }
15135         return cookies;
15136     },
15137
15138     // private
15139     setCookie : function(name, value){
15140         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15141            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15142            ((this.path == null) ? "" : ("; path=" + this.path)) +
15143            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15144            ((this.secure == true) ? "; secure" : "");
15145     },
15146
15147     // private
15148     clearCookie : function(name){
15149         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15150            ((this.path == null) ? "" : ("; path=" + this.path)) +
15151            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15152            ((this.secure == true) ? "; secure" : "");
15153     }
15154 });/*
15155  * Based on:
15156  * Ext JS Library 1.1.1
15157  * Copyright(c) 2006-2007, Ext JS, LLC.
15158  *
15159  * Originally Released Under LGPL - original licence link has changed is not relivant.
15160  *
15161  * Fork - LGPL
15162  * <script type="text/javascript">
15163  */
15164  
15165
15166 /**
15167  * @class Roo.ComponentMgr
15168  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15169  * @singleton
15170  */
15171 Roo.ComponentMgr = function(){
15172     var all = new Roo.util.MixedCollection();
15173
15174     return {
15175         /**
15176          * Registers a component.
15177          * @param {Roo.Component} c The component
15178          */
15179         register : function(c){
15180             all.add(c);
15181         },
15182
15183         /**
15184          * Unregisters a component.
15185          * @param {Roo.Component} c The component
15186          */
15187         unregister : function(c){
15188             all.remove(c);
15189         },
15190
15191         /**
15192          * Returns a component by id
15193          * @param {String} id The component id
15194          */
15195         get : function(id){
15196             return all.get(id);
15197         },
15198
15199         /**
15200          * Registers a function that will be called when a specified component is added to ComponentMgr
15201          * @param {String} id The component id
15202          * @param {Funtction} fn The callback function
15203          * @param {Object} scope The scope of the callback
15204          */
15205         onAvailable : function(id, fn, scope){
15206             all.on("add", function(index, o){
15207                 if(o.id == id){
15208                     fn.call(scope || o, o);
15209                     all.un("add", fn, scope);
15210                 }
15211             });
15212         }
15213     };
15214 }();/*
15215  * Based on:
15216  * Ext JS Library 1.1.1
15217  * Copyright(c) 2006-2007, Ext JS, LLC.
15218  *
15219  * Originally Released Under LGPL - original licence link has changed is not relivant.
15220  *
15221  * Fork - LGPL
15222  * <script type="text/javascript">
15223  */
15224  
15225 /**
15226  * @class Roo.Component
15227  * @extends Roo.util.Observable
15228  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15229  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15230  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15231  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15232  * All visual components (widgets) that require rendering into a layout should subclass Component.
15233  * @constructor
15234  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15235  * 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
15236  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15237  */
15238 Roo.Component = function(config){
15239     config = config || {};
15240     if(config.tagName || config.dom || typeof config == "string"){ // element object
15241         config = {el: config, id: config.id || config};
15242     }
15243     this.initialConfig = config;
15244
15245     Roo.apply(this, config);
15246     this.addEvents({
15247         /**
15248          * @event disable
15249          * Fires after the component is disabled.
15250              * @param {Roo.Component} this
15251              */
15252         disable : true,
15253         /**
15254          * @event enable
15255          * Fires after the component is enabled.
15256              * @param {Roo.Component} this
15257              */
15258         enable : true,
15259         /**
15260          * @event beforeshow
15261          * Fires before the component is shown.  Return false to stop the show.
15262              * @param {Roo.Component} this
15263              */
15264         beforeshow : true,
15265         /**
15266          * @event show
15267          * Fires after the component is shown.
15268              * @param {Roo.Component} this
15269              */
15270         show : true,
15271         /**
15272          * @event beforehide
15273          * Fires before the component is hidden. Return false to stop the hide.
15274              * @param {Roo.Component} this
15275              */
15276         beforehide : true,
15277         /**
15278          * @event hide
15279          * Fires after the component is hidden.
15280              * @param {Roo.Component} this
15281              */
15282         hide : true,
15283         /**
15284          * @event beforerender
15285          * Fires before the component is rendered. Return false to stop the render.
15286              * @param {Roo.Component} this
15287              */
15288         beforerender : true,
15289         /**
15290          * @event render
15291          * Fires after the component is rendered.
15292              * @param {Roo.Component} this
15293              */
15294         render : true,
15295         /**
15296          * @event beforedestroy
15297          * Fires before the component is destroyed. Return false to stop the destroy.
15298              * @param {Roo.Component} this
15299              */
15300         beforedestroy : true,
15301         /**
15302          * @event destroy
15303          * Fires after the component is destroyed.
15304              * @param {Roo.Component} this
15305              */
15306         destroy : true
15307     });
15308     if(!this.id){
15309         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15310     }
15311     Roo.ComponentMgr.register(this);
15312     Roo.Component.superclass.constructor.call(this);
15313     this.initComponent();
15314     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15315         this.render(this.renderTo);
15316         delete this.renderTo;
15317     }
15318 };
15319
15320 /** @private */
15321 Roo.Component.AUTO_ID = 1000;
15322
15323 Roo.extend(Roo.Component, Roo.util.Observable, {
15324     /**
15325      * @scope Roo.Component.prototype
15326      * @type {Boolean}
15327      * true if this component is hidden. Read-only.
15328      */
15329     hidden : false,
15330     /**
15331      * @type {Boolean}
15332      * true if this component is disabled. Read-only.
15333      */
15334     disabled : false,
15335     /**
15336      * @type {Boolean}
15337      * true if this component has been rendered. Read-only.
15338      */
15339     rendered : false,
15340     
15341     /** @cfg {String} disableClass
15342      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15343      */
15344     disabledClass : "x-item-disabled",
15345         /** @cfg {Boolean} allowDomMove
15346          * Whether the component can move the Dom node when rendering (defaults to true).
15347          */
15348     allowDomMove : true,
15349     /** @cfg {String} hideMode (display|visibility)
15350      * How this component should hidden. Supported values are
15351      * "visibility" (css visibility), "offsets" (negative offset position) and
15352      * "display" (css display) - defaults to "display".
15353      */
15354     hideMode: 'display',
15355
15356     /** @private */
15357     ctype : "Roo.Component",
15358
15359     /**
15360      * @cfg {String} actionMode 
15361      * which property holds the element that used for  hide() / show() / disable() / enable()
15362      * default is 'el' 
15363      */
15364     actionMode : "el",
15365
15366     /** @private */
15367     getActionEl : function(){
15368         return this[this.actionMode];
15369     },
15370
15371     initComponent : Roo.emptyFn,
15372     /**
15373      * If this is a lazy rendering component, render it to its container element.
15374      * @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.
15375      */
15376     render : function(container, position){
15377         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15378             if(!container && this.el){
15379                 this.el = Roo.get(this.el);
15380                 container = this.el.dom.parentNode;
15381                 this.allowDomMove = false;
15382             }
15383             this.container = Roo.get(container);
15384             this.rendered = true;
15385             if(position !== undefined){
15386                 if(typeof position == 'number'){
15387                     position = this.container.dom.childNodes[position];
15388                 }else{
15389                     position = Roo.getDom(position);
15390                 }
15391             }
15392             this.onRender(this.container, position || null);
15393             if(this.cls){
15394                 this.el.addClass(this.cls);
15395                 delete this.cls;
15396             }
15397             if(this.style){
15398                 this.el.applyStyles(this.style);
15399                 delete this.style;
15400             }
15401             this.fireEvent("render", this);
15402             this.afterRender(this.container);
15403             if(this.hidden){
15404                 this.hide();
15405             }
15406             if(this.disabled){
15407                 this.disable();
15408             }
15409         }
15410         return this;
15411     },
15412
15413     /** @private */
15414     // default function is not really useful
15415     onRender : function(ct, position){
15416         if(this.el){
15417             this.el = Roo.get(this.el);
15418             if(this.allowDomMove !== false){
15419                 ct.dom.insertBefore(this.el.dom, position);
15420             }
15421         }
15422     },
15423
15424     /** @private */
15425     getAutoCreate : function(){
15426         var cfg = typeof this.autoCreate == "object" ?
15427                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15428         if(this.id && !cfg.id){
15429             cfg.id = this.id;
15430         }
15431         return cfg;
15432     },
15433
15434     /** @private */
15435     afterRender : Roo.emptyFn,
15436
15437     /**
15438      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15439      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15440      */
15441     destroy : function(){
15442         if(this.fireEvent("beforedestroy", this) !== false){
15443             this.purgeListeners();
15444             this.beforeDestroy();
15445             if(this.rendered){
15446                 this.el.removeAllListeners();
15447                 this.el.remove();
15448                 if(this.actionMode == "container"){
15449                     this.container.remove();
15450                 }
15451             }
15452             this.onDestroy();
15453             Roo.ComponentMgr.unregister(this);
15454             this.fireEvent("destroy", this);
15455         }
15456     },
15457
15458         /** @private */
15459     beforeDestroy : function(){
15460
15461     },
15462
15463         /** @private */
15464         onDestroy : function(){
15465
15466     },
15467
15468     /**
15469      * Returns the underlying {@link Roo.Element}.
15470      * @return {Roo.Element} The element
15471      */
15472     getEl : function(){
15473         return this.el;
15474     },
15475
15476     /**
15477      * Returns the id of this component.
15478      * @return {String}
15479      */
15480     getId : function(){
15481         return this.id;
15482     },
15483
15484     /**
15485      * Try to focus this component.
15486      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15487      * @return {Roo.Component} this
15488      */
15489     focus : function(selectText){
15490         if(this.rendered){
15491             this.el.focus();
15492             if(selectText === true){
15493                 this.el.dom.select();
15494             }
15495         }
15496         return this;
15497     },
15498
15499     /** @private */
15500     blur : function(){
15501         if(this.rendered){
15502             this.el.blur();
15503         }
15504         return this;
15505     },
15506
15507     /**
15508      * Disable this component.
15509      * @return {Roo.Component} this
15510      */
15511     disable : function(){
15512         if(this.rendered){
15513             this.onDisable();
15514         }
15515         this.disabled = true;
15516         this.fireEvent("disable", this);
15517         return this;
15518     },
15519
15520         // private
15521     onDisable : function(){
15522         this.getActionEl().addClass(this.disabledClass);
15523         this.el.dom.disabled = true;
15524     },
15525
15526     /**
15527      * Enable this component.
15528      * @return {Roo.Component} this
15529      */
15530     enable : function(){
15531         if(this.rendered){
15532             this.onEnable();
15533         }
15534         this.disabled = false;
15535         this.fireEvent("enable", this);
15536         return this;
15537     },
15538
15539         // private
15540     onEnable : function(){
15541         this.getActionEl().removeClass(this.disabledClass);
15542         this.el.dom.disabled = false;
15543     },
15544
15545     /**
15546      * Convenience function for setting disabled/enabled by boolean.
15547      * @param {Boolean} disabled
15548      */
15549     setDisabled : function(disabled){
15550         this[disabled ? "disable" : "enable"]();
15551     },
15552
15553     /**
15554      * Show this component.
15555      * @return {Roo.Component} this
15556      */
15557     show: function(){
15558         if(this.fireEvent("beforeshow", this) !== false){
15559             this.hidden = false;
15560             if(this.rendered){
15561                 this.onShow();
15562             }
15563             this.fireEvent("show", this);
15564         }
15565         return this;
15566     },
15567
15568     // private
15569     onShow : function(){
15570         var ae = this.getActionEl();
15571         if(this.hideMode == 'visibility'){
15572             ae.dom.style.visibility = "visible";
15573         }else if(this.hideMode == 'offsets'){
15574             ae.removeClass('x-hidden');
15575         }else{
15576             ae.dom.style.display = "";
15577         }
15578     },
15579
15580     /**
15581      * Hide this component.
15582      * @return {Roo.Component} this
15583      */
15584     hide: function(){
15585         if(this.fireEvent("beforehide", this) !== false){
15586             this.hidden = true;
15587             if(this.rendered){
15588                 this.onHide();
15589             }
15590             this.fireEvent("hide", this);
15591         }
15592         return this;
15593     },
15594
15595     // private
15596     onHide : function(){
15597         var ae = this.getActionEl();
15598         if(this.hideMode == 'visibility'){
15599             ae.dom.style.visibility = "hidden";
15600         }else if(this.hideMode == 'offsets'){
15601             ae.addClass('x-hidden');
15602         }else{
15603             ae.dom.style.display = "none";
15604         }
15605     },
15606
15607     /**
15608      * Convenience function to hide or show this component by boolean.
15609      * @param {Boolean} visible True to show, false to hide
15610      * @return {Roo.Component} this
15611      */
15612     setVisible: function(visible){
15613         if(visible) {
15614             this.show();
15615         }else{
15616             this.hide();
15617         }
15618         return this;
15619     },
15620
15621     /**
15622      * Returns true if this component is visible.
15623      */
15624     isVisible : function(){
15625         return this.getActionEl().isVisible();
15626     },
15627
15628     cloneConfig : function(overrides){
15629         overrides = overrides || {};
15630         var id = overrides.id || Roo.id();
15631         var cfg = Roo.applyIf(overrides, this.initialConfig);
15632         cfg.id = id; // prevent dup id
15633         return new this.constructor(cfg);
15634     }
15635 });/*
15636  * Based on:
15637  * Ext JS Library 1.1.1
15638  * Copyright(c) 2006-2007, Ext JS, LLC.
15639  *
15640  * Originally Released Under LGPL - original licence link has changed is not relivant.
15641  *
15642  * Fork - LGPL
15643  * <script type="text/javascript">
15644  */
15645
15646 /**
15647  * @class Roo.BoxComponent
15648  * @extends Roo.Component
15649  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15650  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15651  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15652  * layout containers.
15653  * @constructor
15654  * @param {Roo.Element/String/Object} config The configuration options.
15655  */
15656 Roo.BoxComponent = function(config){
15657     Roo.Component.call(this, config);
15658     this.addEvents({
15659         /**
15660          * @event resize
15661          * Fires after the component is resized.
15662              * @param {Roo.Component} this
15663              * @param {Number} adjWidth The box-adjusted width that was set
15664              * @param {Number} adjHeight The box-adjusted height that was set
15665              * @param {Number} rawWidth The width that was originally specified
15666              * @param {Number} rawHeight The height that was originally specified
15667              */
15668         resize : true,
15669         /**
15670          * @event move
15671          * Fires after the component is moved.
15672              * @param {Roo.Component} this
15673              * @param {Number} x The new x position
15674              * @param {Number} y The new y position
15675              */
15676         move : true
15677     });
15678 };
15679
15680 Roo.extend(Roo.BoxComponent, Roo.Component, {
15681     // private, set in afterRender to signify that the component has been rendered
15682     boxReady : false,
15683     // private, used to defer height settings to subclasses
15684     deferHeight: false,
15685     /** @cfg {Number} width
15686      * width (optional) size of component
15687      */
15688      /** @cfg {Number} height
15689      * height (optional) size of component
15690      */
15691      
15692     /**
15693      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15694      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15695      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15696      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15697      * @return {Roo.BoxComponent} this
15698      */
15699     setSize : function(w, h){
15700         // support for standard size objects
15701         if(typeof w == 'object'){
15702             h = w.height;
15703             w = w.width;
15704         }
15705         // not rendered
15706         if(!this.boxReady){
15707             this.width = w;
15708             this.height = h;
15709             return this;
15710         }
15711
15712         // prevent recalcs when not needed
15713         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15714             return this;
15715         }
15716         this.lastSize = {width: w, height: h};
15717
15718         var adj = this.adjustSize(w, h);
15719         var aw = adj.width, ah = adj.height;
15720         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15721             var rz = this.getResizeEl();
15722             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15723                 rz.setSize(aw, ah);
15724             }else if(!this.deferHeight && ah !== undefined){
15725                 rz.setHeight(ah);
15726             }else if(aw !== undefined){
15727                 rz.setWidth(aw);
15728             }
15729             this.onResize(aw, ah, w, h);
15730             this.fireEvent('resize', this, aw, ah, w, h);
15731         }
15732         return this;
15733     },
15734
15735     /**
15736      * Gets the current size of the component's underlying element.
15737      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15738      */
15739     getSize : function(){
15740         return this.el.getSize();
15741     },
15742
15743     /**
15744      * Gets the current XY position of the component's underlying element.
15745      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15746      * @return {Array} The XY position of the element (e.g., [100, 200])
15747      */
15748     getPosition : function(local){
15749         if(local === true){
15750             return [this.el.getLeft(true), this.el.getTop(true)];
15751         }
15752         return this.xy || this.el.getXY();
15753     },
15754
15755     /**
15756      * Gets the current box measurements of the component's underlying element.
15757      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15758      * @returns {Object} box An object in the format {x, y, width, height}
15759      */
15760     getBox : function(local){
15761         var s = this.el.getSize();
15762         if(local){
15763             s.x = this.el.getLeft(true);
15764             s.y = this.el.getTop(true);
15765         }else{
15766             var xy = this.xy || this.el.getXY();
15767             s.x = xy[0];
15768             s.y = xy[1];
15769         }
15770         return s;
15771     },
15772
15773     /**
15774      * Sets the current box measurements of the component's underlying element.
15775      * @param {Object} box An object in the format {x, y, width, height}
15776      * @returns {Roo.BoxComponent} this
15777      */
15778     updateBox : function(box){
15779         this.setSize(box.width, box.height);
15780         this.setPagePosition(box.x, box.y);
15781         return this;
15782     },
15783
15784     // protected
15785     getResizeEl : function(){
15786         return this.resizeEl || this.el;
15787     },
15788
15789     // protected
15790     getPositionEl : function(){
15791         return this.positionEl || this.el;
15792     },
15793
15794     /**
15795      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15796      * This method fires the move event.
15797      * @param {Number} left The new left
15798      * @param {Number} top The new top
15799      * @returns {Roo.BoxComponent} this
15800      */
15801     setPosition : function(x, y){
15802         this.x = x;
15803         this.y = y;
15804         if(!this.boxReady){
15805             return this;
15806         }
15807         var adj = this.adjustPosition(x, y);
15808         var ax = adj.x, ay = adj.y;
15809
15810         var el = this.getPositionEl();
15811         if(ax !== undefined || ay !== undefined){
15812             if(ax !== undefined && ay !== undefined){
15813                 el.setLeftTop(ax, ay);
15814             }else if(ax !== undefined){
15815                 el.setLeft(ax);
15816             }else if(ay !== undefined){
15817                 el.setTop(ay);
15818             }
15819             this.onPosition(ax, ay);
15820             this.fireEvent('move', this, ax, ay);
15821         }
15822         return this;
15823     },
15824
15825     /**
15826      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15827      * This method fires the move event.
15828      * @param {Number} x The new x position
15829      * @param {Number} y The new y position
15830      * @returns {Roo.BoxComponent} this
15831      */
15832     setPagePosition : function(x, y){
15833         this.pageX = x;
15834         this.pageY = y;
15835         if(!this.boxReady){
15836             return;
15837         }
15838         if(x === undefined || y === undefined){ // cannot translate undefined points
15839             return;
15840         }
15841         var p = this.el.translatePoints(x, y);
15842         this.setPosition(p.left, p.top);
15843         return this;
15844     },
15845
15846     // private
15847     onRender : function(ct, position){
15848         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15849         if(this.resizeEl){
15850             this.resizeEl = Roo.get(this.resizeEl);
15851         }
15852         if(this.positionEl){
15853             this.positionEl = Roo.get(this.positionEl);
15854         }
15855     },
15856
15857     // private
15858     afterRender : function(){
15859         Roo.BoxComponent.superclass.afterRender.call(this);
15860         this.boxReady = true;
15861         this.setSize(this.width, this.height);
15862         if(this.x || this.y){
15863             this.setPosition(this.x, this.y);
15864         }
15865         if(this.pageX || this.pageY){
15866             this.setPagePosition(this.pageX, this.pageY);
15867         }
15868     },
15869
15870     /**
15871      * Force the component's size to recalculate based on the underlying element's current height and width.
15872      * @returns {Roo.BoxComponent} this
15873      */
15874     syncSize : function(){
15875         delete this.lastSize;
15876         this.setSize(this.el.getWidth(), this.el.getHeight());
15877         return this;
15878     },
15879
15880     /**
15881      * Called after the component is resized, this method is empty by default but can be implemented by any
15882      * subclass that needs to perform custom logic after a resize occurs.
15883      * @param {Number} adjWidth The box-adjusted width that was set
15884      * @param {Number} adjHeight The box-adjusted height that was set
15885      * @param {Number} rawWidth The width that was originally specified
15886      * @param {Number} rawHeight The height that was originally specified
15887      */
15888     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15889
15890     },
15891
15892     /**
15893      * Called after the component is moved, this method is empty by default but can be implemented by any
15894      * subclass that needs to perform custom logic after a move occurs.
15895      * @param {Number} x The new x position
15896      * @param {Number} y The new y position
15897      */
15898     onPosition : function(x, y){
15899
15900     },
15901
15902     // private
15903     adjustSize : function(w, h){
15904         if(this.autoWidth){
15905             w = 'auto';
15906         }
15907         if(this.autoHeight){
15908             h = 'auto';
15909         }
15910         return {width : w, height: h};
15911     },
15912
15913     // private
15914     adjustPosition : function(x, y){
15915         return {x : x, y: y};
15916     }
15917 });/*
15918  * Original code for Roojs - LGPL
15919  * <script type="text/javascript">
15920  */
15921  
15922 /**
15923  * @class Roo.XComponent
15924  * A delayed Element creator...
15925  * Or a way to group chunks of interface together.
15926  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15927  *  used in conjunction with XComponent.build() it will create an instance of each element,
15928  *  then call addxtype() to build the User interface.
15929  * 
15930  * Mypart.xyx = new Roo.XComponent({
15931
15932     parent : 'Mypart.xyz', // empty == document.element.!!
15933     order : '001',
15934     name : 'xxxx'
15935     region : 'xxxx'
15936     disabled : function() {} 
15937      
15938     tree : function() { // return an tree of xtype declared components
15939         var MODULE = this;
15940         return 
15941         {
15942             xtype : 'NestedLayoutPanel',
15943             // technicall
15944         }
15945      ]
15946  *})
15947  *
15948  *
15949  * It can be used to build a big heiracy, with parent etc.
15950  * or you can just use this to render a single compoent to a dom element
15951  * MYPART.render(Roo.Element | String(id) | dom_element )
15952  *
15953  *
15954  * Usage patterns.
15955  *
15956  * Classic Roo
15957  *
15958  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15959  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15960  *
15961  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15962  *
15963  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15964  * - if mulitple topModules exist, the last one is defined as the top module.
15965  *
15966  * Embeded Roo
15967  * 
15968  * When the top level or multiple modules are to embedded into a existing HTML page,
15969  * the parent element can container '#id' of the element where the module will be drawn.
15970  *
15971  * Bootstrap Roo
15972  *
15973  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15974  * it relies more on a include mechanism, where sub modules are included into an outer page.
15975  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15976  * 
15977  * Bootstrap Roo Included elements
15978  *
15979  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15980  * hence confusing the component builder as it thinks there are multiple top level elements. 
15981  *
15982  * 
15983  * 
15984  * @extends Roo.util.Observable
15985  * @constructor
15986  * @param cfg {Object} configuration of component
15987  * 
15988  */
15989 Roo.XComponent = function(cfg) {
15990     Roo.apply(this, cfg);
15991     this.addEvents({ 
15992         /**
15993              * @event built
15994              * Fires when this the componnt is built
15995              * @param {Roo.XComponent} c the component
15996              */
15997         'built' : true
15998         
15999     });
16000     this.region = this.region || 'center'; // default..
16001     Roo.XComponent.register(this);
16002     this.modules = false;
16003     this.el = false; // where the layout goes..
16004     
16005     
16006 }
16007 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16008     /**
16009      * @property el
16010      * The created element (with Roo.factory())
16011      * @type {Roo.Layout}
16012      */
16013     el  : false,
16014     
16015     /**
16016      * @property el
16017      * for BC  - use el in new code
16018      * @type {Roo.Layout}
16019      */
16020     panel : false,
16021     
16022     /**
16023      * @property layout
16024      * for BC  - use el in new code
16025      * @type {Roo.Layout}
16026      */
16027     layout : false,
16028     
16029      /**
16030      * @cfg {Function|boolean} disabled
16031      * If this module is disabled by some rule, return true from the funtion
16032      */
16033     disabled : false,
16034     
16035     /**
16036      * @cfg {String} parent 
16037      * Name of parent element which it get xtype added to..
16038      */
16039     parent: false,
16040     
16041     /**
16042      * @cfg {String} order
16043      * Used to set the order in which elements are created (usefull for multiple tabs)
16044      */
16045     
16046     order : false,
16047     /**
16048      * @cfg {String} name
16049      * String to display while loading.
16050      */
16051     name : false,
16052     /**
16053      * @cfg {String} region
16054      * Region to render component to (defaults to center)
16055      */
16056     region : 'center',
16057     
16058     /**
16059      * @cfg {Array} items
16060      * A single item array - the first element is the root of the tree..
16061      * It's done this way to stay compatible with the Xtype system...
16062      */
16063     items : false,
16064     
16065     /**
16066      * @property _tree
16067      * The method that retuns the tree of parts that make up this compoennt 
16068      * @type {function}
16069      */
16070     _tree  : false,
16071     
16072      /**
16073      * render
16074      * render element to dom or tree
16075      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16076      */
16077     
16078     render : function(el)
16079     {
16080         
16081         el = el || false;
16082         var hp = this.parent ? 1 : 0;
16083         Roo.debug &&  Roo.log(this);
16084         
16085         var tree = this._tree ? this._tree() : this.tree();
16086
16087         
16088         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16089             // if parent is a '#.....' string, then let's use that..
16090             var ename = this.parent.substr(1);
16091             this.parent = false;
16092             Roo.debug && Roo.log(ename);
16093             switch (ename) {
16094                 case 'bootstrap-body':
16095                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16096                         // this is the BorderLayout standard?
16097                        this.parent = { el : true };
16098                        break;
16099                     }
16100                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16101                         // need to insert stuff...
16102                         this.parent =  {
16103                              el : new Roo.bootstrap.layout.Border({
16104                                  el : document.body, 
16105                      
16106                                  center: {
16107                                     titlebar: false,
16108                                     autoScroll:false,
16109                                     closeOnTab: true,
16110                                     tabPosition: 'top',
16111                                       //resizeTabs: true,
16112                                     alwaysShowTabs: true,
16113                                     hideTabs: false
16114                                      //minTabWidth: 140
16115                                  }
16116                              })
16117                         
16118                          };
16119                          break;
16120                     }
16121                          
16122                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16123                         this.parent = { el :  new  Roo.bootstrap.Body() };
16124                         Roo.debug && Roo.log("setting el to doc body");
16125                          
16126                     } else {
16127                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16128                     }
16129                     break;
16130                 case 'bootstrap':
16131                     this.parent = { el : true};
16132                     // fall through
16133                 default:
16134                     el = Roo.get(ename);
16135                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16136                         this.parent = { el : true};
16137                     }
16138                     
16139                     break;
16140             }
16141                 
16142             
16143             if (!el && !this.parent) {
16144                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16145                 return;
16146             }
16147         }
16148         
16149         Roo.debug && Roo.log("EL:");
16150         Roo.debug && Roo.log(el);
16151         Roo.debug && Roo.log("this.parent.el:");
16152         Roo.debug && Roo.log(this.parent.el);
16153         
16154
16155         // altertive root elements ??? - we need a better way to indicate these.
16156         var is_alt = Roo.XComponent.is_alt ||
16157                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16158                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16159                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16160         
16161         
16162         
16163         if (!this.parent && is_alt) {
16164             //el = Roo.get(document.body);
16165             this.parent = { el : true };
16166         }
16167             
16168             
16169         
16170         if (!this.parent) {
16171             
16172             Roo.debug && Roo.log("no parent - creating one");
16173             
16174             el = el ? Roo.get(el) : false;      
16175             
16176             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16177                 
16178                 this.parent =  {
16179                     el : new Roo.bootstrap.layout.Border({
16180                         el: el || document.body,
16181                     
16182                         center: {
16183                             titlebar: false,
16184                             autoScroll:false,
16185                             closeOnTab: true,
16186                             tabPosition: 'top',
16187                              //resizeTabs: true,
16188                             alwaysShowTabs: false,
16189                             hideTabs: true,
16190                             minTabWidth: 140,
16191                             overflow: 'visible'
16192                          }
16193                      })
16194                 };
16195             } else {
16196             
16197                 // it's a top level one..
16198                 this.parent =  {
16199                     el : new Roo.BorderLayout(el || document.body, {
16200                         center: {
16201                             titlebar: false,
16202                             autoScroll:false,
16203                             closeOnTab: true,
16204                             tabPosition: 'top',
16205                              //resizeTabs: true,
16206                             alwaysShowTabs: el && hp? false :  true,
16207                             hideTabs: el || !hp ? true :  false,
16208                             minTabWidth: 140
16209                          }
16210                     })
16211                 };
16212             }
16213         }
16214         
16215         if (!this.parent.el) {
16216                 // probably an old style ctor, which has been disabled.
16217                 return;
16218
16219         }
16220                 // The 'tree' method is  '_tree now' 
16221             
16222         tree.region = tree.region || this.region;
16223         var is_body = false;
16224         if (this.parent.el === true) {
16225             // bootstrap... - body..
16226             if (el) {
16227                 tree.el = el;
16228             }
16229             this.parent.el = Roo.factory(tree);
16230             is_body = true;
16231         }
16232         
16233         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16234         this.fireEvent('built', this);
16235         
16236         this.panel = this.el;
16237         this.layout = this.panel.layout;
16238         this.parentLayout = this.parent.layout  || false;  
16239          
16240     }
16241     
16242 });
16243
16244 Roo.apply(Roo.XComponent, {
16245     /**
16246      * @property  hideProgress
16247      * true to disable the building progress bar.. usefull on single page renders.
16248      * @type Boolean
16249      */
16250     hideProgress : false,
16251     /**
16252      * @property  buildCompleted
16253      * True when the builder has completed building the interface.
16254      * @type Boolean
16255      */
16256     buildCompleted : false,
16257      
16258     /**
16259      * @property  topModule
16260      * the upper most module - uses document.element as it's constructor.
16261      * @type Object
16262      */
16263      
16264     topModule  : false,
16265       
16266     /**
16267      * @property  modules
16268      * array of modules to be created by registration system.
16269      * @type {Array} of Roo.XComponent
16270      */
16271     
16272     modules : [],
16273     /**
16274      * @property  elmodules
16275      * array of modules to be created by which use #ID 
16276      * @type {Array} of Roo.XComponent
16277      */
16278      
16279     elmodules : [],
16280
16281      /**
16282      * @property  is_alt
16283      * Is an alternative Root - normally used by bootstrap or other systems,
16284      *    where the top element in the tree can wrap 'body' 
16285      * @type {boolean}  (default false)
16286      */
16287      
16288     is_alt : false,
16289     /**
16290      * @property  build_from_html
16291      * Build elements from html - used by bootstrap HTML stuff 
16292      *    - this is cleared after build is completed
16293      * @type {boolean}    (default false)
16294      */
16295      
16296     build_from_html : false,
16297     /**
16298      * Register components to be built later.
16299      *
16300      * This solves the following issues
16301      * - Building is not done on page load, but after an authentication process has occured.
16302      * - Interface elements are registered on page load
16303      * - Parent Interface elements may not be loaded before child, so this handles that..
16304      * 
16305      *
16306      * example:
16307      * 
16308      * MyApp.register({
16309           order : '000001',
16310           module : 'Pman.Tab.projectMgr',
16311           region : 'center',
16312           parent : 'Pman.layout',
16313           disabled : false,  // or use a function..
16314         })
16315      
16316      * * @param {Object} details about module
16317      */
16318     register : function(obj) {
16319                 
16320         Roo.XComponent.event.fireEvent('register', obj);
16321         switch(typeof(obj.disabled) ) {
16322                 
16323             case 'undefined':
16324                 break;
16325             
16326             case 'function':
16327                 if ( obj.disabled() ) {
16328                         return;
16329                 }
16330                 break;
16331             
16332             default:
16333                 if (obj.disabled) {
16334                         return;
16335                 }
16336                 break;
16337         }
16338                 
16339         this.modules.push(obj);
16340          
16341     },
16342     /**
16343      * convert a string to an object..
16344      * eg. 'AAA.BBB' -> finds AAA.BBB
16345
16346      */
16347     
16348     toObject : function(str)
16349     {
16350         if (!str || typeof(str) == 'object') {
16351             return str;
16352         }
16353         if (str.substring(0,1) == '#') {
16354             return str;
16355         }
16356
16357         var ar = str.split('.');
16358         var rt, o;
16359         rt = ar.shift();
16360             /** eval:var:o */
16361         try {
16362             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16363         } catch (e) {
16364             throw "Module not found : " + str;
16365         }
16366         
16367         if (o === false) {
16368             throw "Module not found : " + str;
16369         }
16370         Roo.each(ar, function(e) {
16371             if (typeof(o[e]) == 'undefined') {
16372                 throw "Module not found : " + str;
16373             }
16374             o = o[e];
16375         });
16376         
16377         return o;
16378         
16379     },
16380     
16381     
16382     /**
16383      * move modules into their correct place in the tree..
16384      * 
16385      */
16386     preBuild : function ()
16387     {
16388         var _t = this;
16389         Roo.each(this.modules , function (obj)
16390         {
16391             Roo.XComponent.event.fireEvent('beforebuild', obj);
16392             
16393             var opar = obj.parent;
16394             try { 
16395                 obj.parent = this.toObject(opar);
16396             } catch(e) {
16397                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16398                 return;
16399             }
16400             
16401             if (!obj.parent) {
16402                 Roo.debug && Roo.log("GOT top level module");
16403                 Roo.debug && Roo.log(obj);
16404                 obj.modules = new Roo.util.MixedCollection(false, 
16405                     function(o) { return o.order + '' }
16406                 );
16407                 this.topModule = obj;
16408                 return;
16409             }
16410                         // parent is a string (usually a dom element name..)
16411             if (typeof(obj.parent) == 'string') {
16412                 this.elmodules.push(obj);
16413                 return;
16414             }
16415             if (obj.parent.constructor != Roo.XComponent) {
16416                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16417             }
16418             if (!obj.parent.modules) {
16419                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16420                     function(o) { return o.order + '' }
16421                 );
16422             }
16423             if (obj.parent.disabled) {
16424                 obj.disabled = true;
16425             }
16426             obj.parent.modules.add(obj);
16427         }, this);
16428     },
16429     
16430      /**
16431      * make a list of modules to build.
16432      * @return {Array} list of modules. 
16433      */ 
16434     
16435     buildOrder : function()
16436     {
16437         var _this = this;
16438         var cmp = function(a,b) {   
16439             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16440         };
16441         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16442             throw "No top level modules to build";
16443         }
16444         
16445         // make a flat list in order of modules to build.
16446         var mods = this.topModule ? [ this.topModule ] : [];
16447                 
16448         
16449         // elmodules (is a list of DOM based modules )
16450         Roo.each(this.elmodules, function(e) {
16451             mods.push(e);
16452             if (!this.topModule &&
16453                 typeof(e.parent) == 'string' &&
16454                 e.parent.substring(0,1) == '#' &&
16455                 Roo.get(e.parent.substr(1))
16456                ) {
16457                 
16458                 _this.topModule = e;
16459             }
16460             
16461         });
16462
16463         
16464         // add modules to their parents..
16465         var addMod = function(m) {
16466             Roo.debug && Roo.log("build Order: add: " + m.name);
16467                 
16468             mods.push(m);
16469             if (m.modules && !m.disabled) {
16470                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16471                 m.modules.keySort('ASC',  cmp );
16472                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16473     
16474                 m.modules.each(addMod);
16475             } else {
16476                 Roo.debug && Roo.log("build Order: no child modules");
16477             }
16478             // not sure if this is used any more..
16479             if (m.finalize) {
16480                 m.finalize.name = m.name + " (clean up) ";
16481                 mods.push(m.finalize);
16482             }
16483             
16484         }
16485         if (this.topModule && this.topModule.modules) { 
16486             this.topModule.modules.keySort('ASC',  cmp );
16487             this.topModule.modules.each(addMod);
16488         } 
16489         return mods;
16490     },
16491     
16492      /**
16493      * Build the registered modules.
16494      * @param {Object} parent element.
16495      * @param {Function} optional method to call after module has been added.
16496      * 
16497      */ 
16498    
16499     build : function(opts) 
16500     {
16501         
16502         if (typeof(opts) != 'undefined') {
16503             Roo.apply(this,opts);
16504         }
16505         
16506         this.preBuild();
16507         var mods = this.buildOrder();
16508       
16509         //this.allmods = mods;
16510         //Roo.debug && Roo.log(mods);
16511         //return;
16512         if (!mods.length) { // should not happen
16513             throw "NO modules!!!";
16514         }
16515         
16516         
16517         var msg = "Building Interface...";
16518         // flash it up as modal - so we store the mask!?
16519         if (!this.hideProgress && Roo.MessageBox) {
16520             Roo.MessageBox.show({ title: 'loading' });
16521             Roo.MessageBox.show({
16522                title: "Please wait...",
16523                msg: msg,
16524                width:450,
16525                progress:true,
16526                closable:false,
16527                modal: false
16528               
16529             });
16530         }
16531         var total = mods.length;
16532         
16533         var _this = this;
16534         var progressRun = function() {
16535             if (!mods.length) {
16536                 Roo.debug && Roo.log('hide?');
16537                 if (!this.hideProgress && Roo.MessageBox) {
16538                     Roo.MessageBox.hide();
16539                 }
16540                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16541                 
16542                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16543                 
16544                 // THE END...
16545                 return false;   
16546             }
16547             
16548             var m = mods.shift();
16549             
16550             
16551             Roo.debug && Roo.log(m);
16552             // not sure if this is supported any more.. - modules that are are just function
16553             if (typeof(m) == 'function') { 
16554                 m.call(this);
16555                 return progressRun.defer(10, _this);
16556             } 
16557             
16558             
16559             msg = "Building Interface " + (total  - mods.length) + 
16560                     " of " + total + 
16561                     (m.name ? (' - ' + m.name) : '');
16562                         Roo.debug && Roo.log(msg);
16563             if (!_this.hideProgress &&  Roo.MessageBox) { 
16564                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16565             }
16566             
16567          
16568             // is the module disabled?
16569             var disabled = (typeof(m.disabled) == 'function') ?
16570                 m.disabled.call(m.module.disabled) : m.disabled;    
16571             
16572             
16573             if (disabled) {
16574                 return progressRun(); // we do not update the display!
16575             }
16576             
16577             // now build 
16578             
16579                         
16580                         
16581             m.render();
16582             // it's 10 on top level, and 1 on others??? why...
16583             return progressRun.defer(10, _this);
16584              
16585         }
16586         progressRun.defer(1, _this);
16587      
16588         
16589         
16590     },
16591         
16592         
16593         /**
16594          * Event Object.
16595          *
16596          *
16597          */
16598         event: false, 
16599     /**
16600          * wrapper for event.on - aliased later..  
16601          * Typically use to register a event handler for register:
16602          *
16603          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16604          *
16605          */
16606     on : false
16607    
16608     
16609     
16610 });
16611
16612 Roo.XComponent.event = new Roo.util.Observable({
16613                 events : { 
16614                         /**
16615                          * @event register
16616                          * Fires when an Component is registered,
16617                          * set the disable property on the Component to stop registration.
16618                          * @param {Roo.XComponent} c the component being registerd.
16619                          * 
16620                          */
16621                         'register' : true,
16622             /**
16623                          * @event beforebuild
16624                          * Fires before each Component is built
16625                          * can be used to apply permissions.
16626                          * @param {Roo.XComponent} c the component being registerd.
16627                          * 
16628                          */
16629                         'beforebuild' : true,
16630                         /**
16631                          * @event buildcomplete
16632                          * Fires on the top level element when all elements have been built
16633                          * @param {Roo.XComponent} the top level component.
16634                          */
16635                         'buildcomplete' : true
16636                         
16637                 }
16638 });
16639
16640 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16641  //
16642  /**
16643  * marked - a markdown parser
16644  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16645  * https://github.com/chjj/marked
16646  */
16647
16648
16649 /**
16650  *
16651  * Roo.Markdown - is a very crude wrapper around marked..
16652  *
16653  * usage:
16654  * 
16655  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16656  * 
16657  * Note: move the sample code to the bottom of this
16658  * file before uncommenting it.
16659  *
16660  */
16661
16662 Roo.Markdown = {};
16663 Roo.Markdown.toHtml = function(text) {
16664     
16665     var c = new Roo.Markdown.marked.setOptions({
16666             renderer: new Roo.Markdown.marked.Renderer(),
16667             gfm: true,
16668             tables: true,
16669             breaks: false,
16670             pedantic: false,
16671             sanitize: false,
16672             smartLists: true,
16673             smartypants: false
16674           });
16675     // A FEW HACKS!!?
16676     
16677     text = text.replace(/\\\n/g,' ');
16678     return Roo.Markdown.marked(text);
16679 };
16680 //
16681 // converter
16682 //
16683 // Wraps all "globals" so that the only thing
16684 // exposed is makeHtml().
16685 //
16686 (function() {
16687     
16688     /**
16689      * Block-Level Grammar
16690      */
16691     
16692     var block = {
16693       newline: /^\n+/,
16694       code: /^( {4}[^\n]+\n*)+/,
16695       fences: noop,
16696       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16697       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16698       nptable: noop,
16699       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16700       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16701       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16702       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16703       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16704       table: noop,
16705       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16706       text: /^[^\n]+/
16707     };
16708     
16709     block.bullet = /(?:[*+-]|\d+\.)/;
16710     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16711     block.item = replace(block.item, 'gm')
16712       (/bull/g, block.bullet)
16713       ();
16714     
16715     block.list = replace(block.list)
16716       (/bull/g, block.bullet)
16717       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16718       ('def', '\\n+(?=' + block.def.source + ')')
16719       ();
16720     
16721     block.blockquote = replace(block.blockquote)
16722       ('def', block.def)
16723       ();
16724     
16725     block._tag = '(?!(?:'
16726       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16727       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16728       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16729     
16730     block.html = replace(block.html)
16731       ('comment', /<!--[\s\S]*?-->/)
16732       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16733       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16734       (/tag/g, block._tag)
16735       ();
16736     
16737     block.paragraph = replace(block.paragraph)
16738       ('hr', block.hr)
16739       ('heading', block.heading)
16740       ('lheading', block.lheading)
16741       ('blockquote', block.blockquote)
16742       ('tag', '<' + block._tag)
16743       ('def', block.def)
16744       ();
16745     
16746     /**
16747      * Normal Block Grammar
16748      */
16749     
16750     block.normal = merge({}, block);
16751     
16752     /**
16753      * GFM Block Grammar
16754      */
16755     
16756     block.gfm = merge({}, block.normal, {
16757       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16758       paragraph: /^/,
16759       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16760     });
16761     
16762     block.gfm.paragraph = replace(block.paragraph)
16763       ('(?!', '(?!'
16764         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16765         + block.list.source.replace('\\1', '\\3') + '|')
16766       ();
16767     
16768     /**
16769      * GFM + Tables Block Grammar
16770      */
16771     
16772     block.tables = merge({}, block.gfm, {
16773       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16774       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16775     });
16776     
16777     /**
16778      * Block Lexer
16779      */
16780     
16781     function Lexer(options) {
16782       this.tokens = [];
16783       this.tokens.links = {};
16784       this.options = options || marked.defaults;
16785       this.rules = block.normal;
16786     
16787       if (this.options.gfm) {
16788         if (this.options.tables) {
16789           this.rules = block.tables;
16790         } else {
16791           this.rules = block.gfm;
16792         }
16793       }
16794     }
16795     
16796     /**
16797      * Expose Block Rules
16798      */
16799     
16800     Lexer.rules = block;
16801     
16802     /**
16803      * Static Lex Method
16804      */
16805     
16806     Lexer.lex = function(src, options) {
16807       var lexer = new Lexer(options);
16808       return lexer.lex(src);
16809     };
16810     
16811     /**
16812      * Preprocessing
16813      */
16814     
16815     Lexer.prototype.lex = function(src) {
16816       src = src
16817         .replace(/\r\n|\r/g, '\n')
16818         .replace(/\t/g, '    ')
16819         .replace(/\u00a0/g, ' ')
16820         .replace(/\u2424/g, '\n');
16821     
16822       return this.token(src, true);
16823     };
16824     
16825     /**
16826      * Lexing
16827      */
16828     
16829     Lexer.prototype.token = function(src, top, bq) {
16830       var src = src.replace(/^ +$/gm, '')
16831         , next
16832         , loose
16833         , cap
16834         , bull
16835         , b
16836         , item
16837         , space
16838         , i
16839         , l;
16840     
16841       while (src) {
16842         // newline
16843         if (cap = this.rules.newline.exec(src)) {
16844           src = src.substring(cap[0].length);
16845           if (cap[0].length > 1) {
16846             this.tokens.push({
16847               type: 'space'
16848             });
16849           }
16850         }
16851     
16852         // code
16853         if (cap = this.rules.code.exec(src)) {
16854           src = src.substring(cap[0].length);
16855           cap = cap[0].replace(/^ {4}/gm, '');
16856           this.tokens.push({
16857             type: 'code',
16858             text: !this.options.pedantic
16859               ? cap.replace(/\n+$/, '')
16860               : cap
16861           });
16862           continue;
16863         }
16864     
16865         // fences (gfm)
16866         if (cap = this.rules.fences.exec(src)) {
16867           src = src.substring(cap[0].length);
16868           this.tokens.push({
16869             type: 'code',
16870             lang: cap[2],
16871             text: cap[3] || ''
16872           });
16873           continue;
16874         }
16875     
16876         // heading
16877         if (cap = this.rules.heading.exec(src)) {
16878           src = src.substring(cap[0].length);
16879           this.tokens.push({
16880             type: 'heading',
16881             depth: cap[1].length,
16882             text: cap[2]
16883           });
16884           continue;
16885         }
16886     
16887         // table no leading pipe (gfm)
16888         if (top && (cap = this.rules.nptable.exec(src))) {
16889           src = src.substring(cap[0].length);
16890     
16891           item = {
16892             type: 'table',
16893             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16894             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16895             cells: cap[3].replace(/\n$/, '').split('\n')
16896           };
16897     
16898           for (i = 0; i < item.align.length; i++) {
16899             if (/^ *-+: *$/.test(item.align[i])) {
16900               item.align[i] = 'right';
16901             } else if (/^ *:-+: *$/.test(item.align[i])) {
16902               item.align[i] = 'center';
16903             } else if (/^ *:-+ *$/.test(item.align[i])) {
16904               item.align[i] = 'left';
16905             } else {
16906               item.align[i] = null;
16907             }
16908           }
16909     
16910           for (i = 0; i < item.cells.length; i++) {
16911             item.cells[i] = item.cells[i].split(/ *\| */);
16912           }
16913     
16914           this.tokens.push(item);
16915     
16916           continue;
16917         }
16918     
16919         // lheading
16920         if (cap = this.rules.lheading.exec(src)) {
16921           src = src.substring(cap[0].length);
16922           this.tokens.push({
16923             type: 'heading',
16924             depth: cap[2] === '=' ? 1 : 2,
16925             text: cap[1]
16926           });
16927           continue;
16928         }
16929     
16930         // hr
16931         if (cap = this.rules.hr.exec(src)) {
16932           src = src.substring(cap[0].length);
16933           this.tokens.push({
16934             type: 'hr'
16935           });
16936           continue;
16937         }
16938     
16939         // blockquote
16940         if (cap = this.rules.blockquote.exec(src)) {
16941           src = src.substring(cap[0].length);
16942     
16943           this.tokens.push({
16944             type: 'blockquote_start'
16945           });
16946     
16947           cap = cap[0].replace(/^ *> ?/gm, '');
16948     
16949           // Pass `top` to keep the current
16950           // "toplevel" state. This is exactly
16951           // how markdown.pl works.
16952           this.token(cap, top, true);
16953     
16954           this.tokens.push({
16955             type: 'blockquote_end'
16956           });
16957     
16958           continue;
16959         }
16960     
16961         // list
16962         if (cap = this.rules.list.exec(src)) {
16963           src = src.substring(cap[0].length);
16964           bull = cap[2];
16965     
16966           this.tokens.push({
16967             type: 'list_start',
16968             ordered: bull.length > 1
16969           });
16970     
16971           // Get each top-level item.
16972           cap = cap[0].match(this.rules.item);
16973     
16974           next = false;
16975           l = cap.length;
16976           i = 0;
16977     
16978           for (; i < l; i++) {
16979             item = cap[i];
16980     
16981             // Remove the list item's bullet
16982             // so it is seen as the next token.
16983             space = item.length;
16984             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
16985     
16986             // Outdent whatever the
16987             // list item contains. Hacky.
16988             if (~item.indexOf('\n ')) {
16989               space -= item.length;
16990               item = !this.options.pedantic
16991                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
16992                 : item.replace(/^ {1,4}/gm, '');
16993             }
16994     
16995             // Determine whether the next list item belongs here.
16996             // Backpedal if it does not belong in this list.
16997             if (this.options.smartLists && i !== l - 1) {
16998               b = block.bullet.exec(cap[i + 1])[0];
16999               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17000                 src = cap.slice(i + 1).join('\n') + src;
17001                 i = l - 1;
17002               }
17003             }
17004     
17005             // Determine whether item is loose or not.
17006             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17007             // for discount behavior.
17008             loose = next || /\n\n(?!\s*$)/.test(item);
17009             if (i !== l - 1) {
17010               next = item.charAt(item.length - 1) === '\n';
17011               if (!loose) { loose = next; }
17012             }
17013     
17014             this.tokens.push({
17015               type: loose
17016                 ? 'loose_item_start'
17017                 : 'list_item_start'
17018             });
17019     
17020             // Recurse.
17021             this.token(item, false, bq);
17022     
17023             this.tokens.push({
17024               type: 'list_item_end'
17025             });
17026           }
17027     
17028           this.tokens.push({
17029             type: 'list_end'
17030           });
17031     
17032           continue;
17033         }
17034     
17035         // html
17036         if (cap = this.rules.html.exec(src)) {
17037           src = src.substring(cap[0].length);
17038           this.tokens.push({
17039             type: this.options.sanitize
17040               ? 'paragraph'
17041               : 'html',
17042             pre: !this.options.sanitizer
17043               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17044             text: cap[0]
17045           });
17046           continue;
17047         }
17048     
17049         // def
17050         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17051           src = src.substring(cap[0].length);
17052           this.tokens.links[cap[1].toLowerCase()] = {
17053             href: cap[2],
17054             title: cap[3]
17055           };
17056           continue;
17057         }
17058     
17059         // table (gfm)
17060         if (top && (cap = this.rules.table.exec(src))) {
17061           src = src.substring(cap[0].length);
17062     
17063           item = {
17064             type: 'table',
17065             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17066             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17067             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17068           };
17069     
17070           for (i = 0; i < item.align.length; i++) {
17071             if (/^ *-+: *$/.test(item.align[i])) {
17072               item.align[i] = 'right';
17073             } else if (/^ *:-+: *$/.test(item.align[i])) {
17074               item.align[i] = 'center';
17075             } else if (/^ *:-+ *$/.test(item.align[i])) {
17076               item.align[i] = 'left';
17077             } else {
17078               item.align[i] = null;
17079             }
17080           }
17081     
17082           for (i = 0; i < item.cells.length; i++) {
17083             item.cells[i] = item.cells[i]
17084               .replace(/^ *\| *| *\| *$/g, '')
17085               .split(/ *\| */);
17086           }
17087     
17088           this.tokens.push(item);
17089     
17090           continue;
17091         }
17092     
17093         // top-level paragraph
17094         if (top && (cap = this.rules.paragraph.exec(src))) {
17095           src = src.substring(cap[0].length);
17096           this.tokens.push({
17097             type: 'paragraph',
17098             text: cap[1].charAt(cap[1].length - 1) === '\n'
17099               ? cap[1].slice(0, -1)
17100               : cap[1]
17101           });
17102           continue;
17103         }
17104     
17105         // text
17106         if (cap = this.rules.text.exec(src)) {
17107           // Top-level should never reach here.
17108           src = src.substring(cap[0].length);
17109           this.tokens.push({
17110             type: 'text',
17111             text: cap[0]
17112           });
17113           continue;
17114         }
17115     
17116         if (src) {
17117           throw new
17118             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17119         }
17120       }
17121     
17122       return this.tokens;
17123     };
17124     
17125     /**
17126      * Inline-Level Grammar
17127      */
17128     
17129     var inline = {
17130       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17131       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17132       url: noop,
17133       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17134       link: /^!?\[(inside)\]\(href\)/,
17135       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17136       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17137       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17138       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17139       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17140       br: /^ {2,}\n(?!\s*$)/,
17141       del: noop,
17142       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17143     };
17144     
17145     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17146     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17147     
17148     inline.link = replace(inline.link)
17149       ('inside', inline._inside)
17150       ('href', inline._href)
17151       ();
17152     
17153     inline.reflink = replace(inline.reflink)
17154       ('inside', inline._inside)
17155       ();
17156     
17157     /**
17158      * Normal Inline Grammar
17159      */
17160     
17161     inline.normal = merge({}, inline);
17162     
17163     /**
17164      * Pedantic Inline Grammar
17165      */
17166     
17167     inline.pedantic = merge({}, inline.normal, {
17168       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17169       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17170     });
17171     
17172     /**
17173      * GFM Inline Grammar
17174      */
17175     
17176     inline.gfm = merge({}, inline.normal, {
17177       escape: replace(inline.escape)('])', '~|])')(),
17178       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17179       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17180       text: replace(inline.text)
17181         (']|', '~]|')
17182         ('|', '|https?://|')
17183         ()
17184     });
17185     
17186     /**
17187      * GFM + Line Breaks Inline Grammar
17188      */
17189     
17190     inline.breaks = merge({}, inline.gfm, {
17191       br: replace(inline.br)('{2,}', '*')(),
17192       text: replace(inline.gfm.text)('{2,}', '*')()
17193     });
17194     
17195     /**
17196      * Inline Lexer & Compiler
17197      */
17198     
17199     function InlineLexer(links, options) {
17200       this.options = options || marked.defaults;
17201       this.links = links;
17202       this.rules = inline.normal;
17203       this.renderer = this.options.renderer || new Renderer;
17204       this.renderer.options = this.options;
17205     
17206       if (!this.links) {
17207         throw new
17208           Error('Tokens array requires a `links` property.');
17209       }
17210     
17211       if (this.options.gfm) {
17212         if (this.options.breaks) {
17213           this.rules = inline.breaks;
17214         } else {
17215           this.rules = inline.gfm;
17216         }
17217       } else if (this.options.pedantic) {
17218         this.rules = inline.pedantic;
17219       }
17220     }
17221     
17222     /**
17223      * Expose Inline Rules
17224      */
17225     
17226     InlineLexer.rules = inline;
17227     
17228     /**
17229      * Static Lexing/Compiling Method
17230      */
17231     
17232     InlineLexer.output = function(src, links, options) {
17233       var inline = new InlineLexer(links, options);
17234       return inline.output(src);
17235     };
17236     
17237     /**
17238      * Lexing/Compiling
17239      */
17240     
17241     InlineLexer.prototype.output = function(src) {
17242       var out = ''
17243         , link
17244         , text
17245         , href
17246         , cap;
17247     
17248       while (src) {
17249         // escape
17250         if (cap = this.rules.escape.exec(src)) {
17251           src = src.substring(cap[0].length);
17252           out += cap[1];
17253           continue;
17254         }
17255     
17256         // autolink
17257         if (cap = this.rules.autolink.exec(src)) {
17258           src = src.substring(cap[0].length);
17259           if (cap[2] === '@') {
17260             text = cap[1].charAt(6) === ':'
17261               ? this.mangle(cap[1].substring(7))
17262               : this.mangle(cap[1]);
17263             href = this.mangle('mailto:') + text;
17264           } else {
17265             text = escape(cap[1]);
17266             href = text;
17267           }
17268           out += this.renderer.link(href, null, text);
17269           continue;
17270         }
17271     
17272         // url (gfm)
17273         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17274           src = src.substring(cap[0].length);
17275           text = escape(cap[1]);
17276           href = text;
17277           out += this.renderer.link(href, null, text);
17278           continue;
17279         }
17280     
17281         // tag
17282         if (cap = this.rules.tag.exec(src)) {
17283           if (!this.inLink && /^<a /i.test(cap[0])) {
17284             this.inLink = true;
17285           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17286             this.inLink = false;
17287           }
17288           src = src.substring(cap[0].length);
17289           out += this.options.sanitize
17290             ? this.options.sanitizer
17291               ? this.options.sanitizer(cap[0])
17292               : escape(cap[0])
17293             : cap[0];
17294           continue;
17295         }
17296     
17297         // link
17298         if (cap = this.rules.link.exec(src)) {
17299           src = src.substring(cap[0].length);
17300           this.inLink = true;
17301           out += this.outputLink(cap, {
17302             href: cap[2],
17303             title: cap[3]
17304           });
17305           this.inLink = false;
17306           continue;
17307         }
17308     
17309         // reflink, nolink
17310         if ((cap = this.rules.reflink.exec(src))
17311             || (cap = this.rules.nolink.exec(src))) {
17312           src = src.substring(cap[0].length);
17313           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17314           link = this.links[link.toLowerCase()];
17315           if (!link || !link.href) {
17316             out += cap[0].charAt(0);
17317             src = cap[0].substring(1) + src;
17318             continue;
17319           }
17320           this.inLink = true;
17321           out += this.outputLink(cap, link);
17322           this.inLink = false;
17323           continue;
17324         }
17325     
17326         // strong
17327         if (cap = this.rules.strong.exec(src)) {
17328           src = src.substring(cap[0].length);
17329           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17330           continue;
17331         }
17332     
17333         // em
17334         if (cap = this.rules.em.exec(src)) {
17335           src = src.substring(cap[0].length);
17336           out += this.renderer.em(this.output(cap[2] || cap[1]));
17337           continue;
17338         }
17339     
17340         // code
17341         if (cap = this.rules.code.exec(src)) {
17342           src = src.substring(cap[0].length);
17343           out += this.renderer.codespan(escape(cap[2], true));
17344           continue;
17345         }
17346     
17347         // br
17348         if (cap = this.rules.br.exec(src)) {
17349           src = src.substring(cap[0].length);
17350           out += this.renderer.br();
17351           continue;
17352         }
17353     
17354         // del (gfm)
17355         if (cap = this.rules.del.exec(src)) {
17356           src = src.substring(cap[0].length);
17357           out += this.renderer.del(this.output(cap[1]));
17358           continue;
17359         }
17360     
17361         // text
17362         if (cap = this.rules.text.exec(src)) {
17363           src = src.substring(cap[0].length);
17364           out += this.renderer.text(escape(this.smartypants(cap[0])));
17365           continue;
17366         }
17367     
17368         if (src) {
17369           throw new
17370             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17371         }
17372       }
17373     
17374       return out;
17375     };
17376     
17377     /**
17378      * Compile Link
17379      */
17380     
17381     InlineLexer.prototype.outputLink = function(cap, link) {
17382       var href = escape(link.href)
17383         , title = link.title ? escape(link.title) : null;
17384     
17385       return cap[0].charAt(0) !== '!'
17386         ? this.renderer.link(href, title, this.output(cap[1]))
17387         : this.renderer.image(href, title, escape(cap[1]));
17388     };
17389     
17390     /**
17391      * Smartypants Transformations
17392      */
17393     
17394     InlineLexer.prototype.smartypants = function(text) {
17395       if (!this.options.smartypants)  { return text; }
17396       return text
17397         // em-dashes
17398         .replace(/---/g, '\u2014')
17399         // en-dashes
17400         .replace(/--/g, '\u2013')
17401         // opening singles
17402         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17403         // closing singles & apostrophes
17404         .replace(/'/g, '\u2019')
17405         // opening doubles
17406         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17407         // closing doubles
17408         .replace(/"/g, '\u201d')
17409         // ellipses
17410         .replace(/\.{3}/g, '\u2026');
17411     };
17412     
17413     /**
17414      * Mangle Links
17415      */
17416     
17417     InlineLexer.prototype.mangle = function(text) {
17418       if (!this.options.mangle) { return text; }
17419       var out = ''
17420         , l = text.length
17421         , i = 0
17422         , ch;
17423     
17424       for (; i < l; i++) {
17425         ch = text.charCodeAt(i);
17426         if (Math.random() > 0.5) {
17427           ch = 'x' + ch.toString(16);
17428         }
17429         out += '&#' + ch + ';';
17430       }
17431     
17432       return out;
17433     };
17434     
17435     /**
17436      * Renderer
17437      */
17438     
17439     function Renderer(options) {
17440       this.options = options || {};
17441     }
17442     
17443     Renderer.prototype.code = function(code, lang, escaped) {
17444       if (this.options.highlight) {
17445         var out = this.options.highlight(code, lang);
17446         if (out != null && out !== code) {
17447           escaped = true;
17448           code = out;
17449         }
17450       } else {
17451             // hack!!! - it's already escapeD?
17452             escaped = true;
17453       }
17454     
17455       if (!lang) {
17456         return '<pre><code>'
17457           + (escaped ? code : escape(code, true))
17458           + '\n</code></pre>';
17459       }
17460     
17461       return '<pre><code class="'
17462         + this.options.langPrefix
17463         + escape(lang, true)
17464         + '">'
17465         + (escaped ? code : escape(code, true))
17466         + '\n</code></pre>\n';
17467     };
17468     
17469     Renderer.prototype.blockquote = function(quote) {
17470       return '<blockquote>\n' + quote + '</blockquote>\n';
17471     };
17472     
17473     Renderer.prototype.html = function(html) {
17474       return html;
17475     };
17476     
17477     Renderer.prototype.heading = function(text, level, raw) {
17478       return '<h'
17479         + level
17480         + ' id="'
17481         + this.options.headerPrefix
17482         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17483         + '">'
17484         + text
17485         + '</h'
17486         + level
17487         + '>\n';
17488     };
17489     
17490     Renderer.prototype.hr = function() {
17491       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17492     };
17493     
17494     Renderer.prototype.list = function(body, ordered) {
17495       var type = ordered ? 'ol' : 'ul';
17496       return '<' + type + '>\n' + body + '</' + type + '>\n';
17497     };
17498     
17499     Renderer.prototype.listitem = function(text) {
17500       return '<li>' + text + '</li>\n';
17501     };
17502     
17503     Renderer.prototype.paragraph = function(text) {
17504       return '<p>' + text + '</p>\n';
17505     };
17506     
17507     Renderer.prototype.table = function(header, body) {
17508       return '<table class="table table-striped">\n'
17509         + '<thead>\n'
17510         + header
17511         + '</thead>\n'
17512         + '<tbody>\n'
17513         + body
17514         + '</tbody>\n'
17515         + '</table>\n';
17516     };
17517     
17518     Renderer.prototype.tablerow = function(content) {
17519       return '<tr>\n' + content + '</tr>\n';
17520     };
17521     
17522     Renderer.prototype.tablecell = function(content, flags) {
17523       var type = flags.header ? 'th' : 'td';
17524       var tag = flags.align
17525         ? '<' + type + ' style="text-align:' + flags.align + '">'
17526         : '<' + type + '>';
17527       return tag + content + '</' + type + '>\n';
17528     };
17529     
17530     // span level renderer
17531     Renderer.prototype.strong = function(text) {
17532       return '<strong>' + text + '</strong>';
17533     };
17534     
17535     Renderer.prototype.em = function(text) {
17536       return '<em>' + text + '</em>';
17537     };
17538     
17539     Renderer.prototype.codespan = function(text) {
17540       return '<code>' + text + '</code>';
17541     };
17542     
17543     Renderer.prototype.br = function() {
17544       return this.options.xhtml ? '<br/>' : '<br>';
17545     };
17546     
17547     Renderer.prototype.del = function(text) {
17548       return '<del>' + text + '</del>';
17549     };
17550     
17551     Renderer.prototype.link = function(href, title, text) {
17552       if (this.options.sanitize) {
17553         try {
17554           var prot = decodeURIComponent(unescape(href))
17555             .replace(/[^\w:]/g, '')
17556             .toLowerCase();
17557         } catch (e) {
17558           return '';
17559         }
17560         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17561           return '';
17562         }
17563       }
17564       var out = '<a href="' + href + '"';
17565       if (title) {
17566         out += ' title="' + title + '"';
17567       }
17568       out += '>' + text + '</a>';
17569       return out;
17570     };
17571     
17572     Renderer.prototype.image = function(href, title, text) {
17573       var out = '<img src="' + href + '" alt="' + text + '"';
17574       if (title) {
17575         out += ' title="' + title + '"';
17576       }
17577       out += this.options.xhtml ? '/>' : '>';
17578       return out;
17579     };
17580     
17581     Renderer.prototype.text = function(text) {
17582       return text;
17583     };
17584     
17585     /**
17586      * Parsing & Compiling
17587      */
17588     
17589     function Parser(options) {
17590       this.tokens = [];
17591       this.token = null;
17592       this.options = options || marked.defaults;
17593       this.options.renderer = this.options.renderer || new Renderer;
17594       this.renderer = this.options.renderer;
17595       this.renderer.options = this.options;
17596     }
17597     
17598     /**
17599      * Static Parse Method
17600      */
17601     
17602     Parser.parse = function(src, options, renderer) {
17603       var parser = new Parser(options, renderer);
17604       return parser.parse(src);
17605     };
17606     
17607     /**
17608      * Parse Loop
17609      */
17610     
17611     Parser.prototype.parse = function(src) {
17612       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17613       this.tokens = src.reverse();
17614     
17615       var out = '';
17616       while (this.next()) {
17617         out += this.tok();
17618       }
17619     
17620       return out;
17621     };
17622     
17623     /**
17624      * Next Token
17625      */
17626     
17627     Parser.prototype.next = function() {
17628       return this.token = this.tokens.pop();
17629     };
17630     
17631     /**
17632      * Preview Next Token
17633      */
17634     
17635     Parser.prototype.peek = function() {
17636       return this.tokens[this.tokens.length - 1] || 0;
17637     };
17638     
17639     /**
17640      * Parse Text Tokens
17641      */
17642     
17643     Parser.prototype.parseText = function() {
17644       var body = this.token.text;
17645     
17646       while (this.peek().type === 'text') {
17647         body += '\n' + this.next().text;
17648       }
17649     
17650       return this.inline.output(body);
17651     };
17652     
17653     /**
17654      * Parse Current Token
17655      */
17656     
17657     Parser.prototype.tok = function() {
17658       switch (this.token.type) {
17659         case 'space': {
17660           return '';
17661         }
17662         case 'hr': {
17663           return this.renderer.hr();
17664         }
17665         case 'heading': {
17666           return this.renderer.heading(
17667             this.inline.output(this.token.text),
17668             this.token.depth,
17669             this.token.text);
17670         }
17671         case 'code': {
17672           return this.renderer.code(this.token.text,
17673             this.token.lang,
17674             this.token.escaped);
17675         }
17676         case 'table': {
17677           var header = ''
17678             , body = ''
17679             , i
17680             , row
17681             , cell
17682             , flags
17683             , j;
17684     
17685           // header
17686           cell = '';
17687           for (i = 0; i < this.token.header.length; i++) {
17688             flags = { header: true, align: this.token.align[i] };
17689             cell += this.renderer.tablecell(
17690               this.inline.output(this.token.header[i]),
17691               { header: true, align: this.token.align[i] }
17692             );
17693           }
17694           header += this.renderer.tablerow(cell);
17695     
17696           for (i = 0; i < this.token.cells.length; i++) {
17697             row = this.token.cells[i];
17698     
17699             cell = '';
17700             for (j = 0; j < row.length; j++) {
17701               cell += this.renderer.tablecell(
17702                 this.inline.output(row[j]),
17703                 { header: false, align: this.token.align[j] }
17704               );
17705             }
17706     
17707             body += this.renderer.tablerow(cell);
17708           }
17709           return this.renderer.table(header, body);
17710         }
17711         case 'blockquote_start': {
17712           var body = '';
17713     
17714           while (this.next().type !== 'blockquote_end') {
17715             body += this.tok();
17716           }
17717     
17718           return this.renderer.blockquote(body);
17719         }
17720         case 'list_start': {
17721           var body = ''
17722             , ordered = this.token.ordered;
17723     
17724           while (this.next().type !== 'list_end') {
17725             body += this.tok();
17726           }
17727     
17728           return this.renderer.list(body, ordered);
17729         }
17730         case 'list_item_start': {
17731           var body = '';
17732     
17733           while (this.next().type !== 'list_item_end') {
17734             body += this.token.type === 'text'
17735               ? this.parseText()
17736               : this.tok();
17737           }
17738     
17739           return this.renderer.listitem(body);
17740         }
17741         case 'loose_item_start': {
17742           var body = '';
17743     
17744           while (this.next().type !== 'list_item_end') {
17745             body += this.tok();
17746           }
17747     
17748           return this.renderer.listitem(body);
17749         }
17750         case 'html': {
17751           var html = !this.token.pre && !this.options.pedantic
17752             ? this.inline.output(this.token.text)
17753             : this.token.text;
17754           return this.renderer.html(html);
17755         }
17756         case 'paragraph': {
17757           return this.renderer.paragraph(this.inline.output(this.token.text));
17758         }
17759         case 'text': {
17760           return this.renderer.paragraph(this.parseText());
17761         }
17762       }
17763     };
17764     
17765     /**
17766      * Helpers
17767      */
17768     
17769     function escape(html, encode) {
17770       return html
17771         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17772         .replace(/</g, '&lt;')
17773         .replace(/>/g, '&gt;')
17774         .replace(/"/g, '&quot;')
17775         .replace(/'/g, '&#39;');
17776     }
17777     
17778     function unescape(html) {
17779         // explicitly match decimal, hex, and named HTML entities 
17780       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17781         n = n.toLowerCase();
17782         if (n === 'colon') { return ':'; }
17783         if (n.charAt(0) === '#') {
17784           return n.charAt(1) === 'x'
17785             ? String.fromCharCode(parseInt(n.substring(2), 16))
17786             : String.fromCharCode(+n.substring(1));
17787         }
17788         return '';
17789       });
17790     }
17791     
17792     function replace(regex, opt) {
17793       regex = regex.source;
17794       opt = opt || '';
17795       return function self(name, val) {
17796         if (!name) { return new RegExp(regex, opt); }
17797         val = val.source || val;
17798         val = val.replace(/(^|[^\[])\^/g, '$1');
17799         regex = regex.replace(name, val);
17800         return self;
17801       };
17802     }
17803     
17804     function noop() {}
17805     noop.exec = noop;
17806     
17807     function merge(obj) {
17808       var i = 1
17809         , target
17810         , key;
17811     
17812       for (; i < arguments.length; i++) {
17813         target = arguments[i];
17814         for (key in target) {
17815           if (Object.prototype.hasOwnProperty.call(target, key)) {
17816             obj[key] = target[key];
17817           }
17818         }
17819       }
17820     
17821       return obj;
17822     }
17823     
17824     
17825     /**
17826      * Marked
17827      */
17828     
17829     function marked(src, opt, callback) {
17830       if (callback || typeof opt === 'function') {
17831         if (!callback) {
17832           callback = opt;
17833           opt = null;
17834         }
17835     
17836         opt = merge({}, marked.defaults, opt || {});
17837     
17838         var highlight = opt.highlight
17839           , tokens
17840           , pending
17841           , i = 0;
17842     
17843         try {
17844           tokens = Lexer.lex(src, opt)
17845         } catch (e) {
17846           return callback(e);
17847         }
17848     
17849         pending = tokens.length;
17850     
17851         var done = function(err) {
17852           if (err) {
17853             opt.highlight = highlight;
17854             return callback(err);
17855           }
17856     
17857           var out;
17858     
17859           try {
17860             out = Parser.parse(tokens, opt);
17861           } catch (e) {
17862             err = e;
17863           }
17864     
17865           opt.highlight = highlight;
17866     
17867           return err
17868             ? callback(err)
17869             : callback(null, out);
17870         };
17871     
17872         if (!highlight || highlight.length < 3) {
17873           return done();
17874         }
17875     
17876         delete opt.highlight;
17877     
17878         if (!pending) { return done(); }
17879     
17880         for (; i < tokens.length; i++) {
17881           (function(token) {
17882             if (token.type !== 'code') {
17883               return --pending || done();
17884             }
17885             return highlight(token.text, token.lang, function(err, code) {
17886               if (err) { return done(err); }
17887               if (code == null || code === token.text) {
17888                 return --pending || done();
17889               }
17890               token.text = code;
17891               token.escaped = true;
17892               --pending || done();
17893             });
17894           })(tokens[i]);
17895         }
17896     
17897         return;
17898       }
17899       try {
17900         if (opt) { opt = merge({}, marked.defaults, opt); }
17901         return Parser.parse(Lexer.lex(src, opt), opt);
17902       } catch (e) {
17903         e.message += '\nPlease report this to https://github.com/chjj/marked.';
17904         if ((opt || marked.defaults).silent) {
17905           return '<p>An error occured:</p><pre>'
17906             + escape(e.message + '', true)
17907             + '</pre>';
17908         }
17909         throw e;
17910       }
17911     }
17912     
17913     /**
17914      * Options
17915      */
17916     
17917     marked.options =
17918     marked.setOptions = function(opt) {
17919       merge(marked.defaults, opt);
17920       return marked;
17921     };
17922     
17923     marked.defaults = {
17924       gfm: true,
17925       tables: true,
17926       breaks: false,
17927       pedantic: false,
17928       sanitize: false,
17929       sanitizer: null,
17930       mangle: true,
17931       smartLists: false,
17932       silent: false,
17933       highlight: null,
17934       langPrefix: 'lang-',
17935       smartypants: false,
17936       headerPrefix: '',
17937       renderer: new Renderer,
17938       xhtml: false
17939     };
17940     
17941     /**
17942      * Expose
17943      */
17944     
17945     marked.Parser = Parser;
17946     marked.parser = Parser.parse;
17947     
17948     marked.Renderer = Renderer;
17949     
17950     marked.Lexer = Lexer;
17951     marked.lexer = Lexer.lex;
17952     
17953     marked.InlineLexer = InlineLexer;
17954     marked.inlineLexer = InlineLexer.output;
17955     
17956     marked.parse = marked;
17957     
17958     Roo.Markdown.marked = marked;
17959
17960 })();/*
17961  * Based on:
17962  * Ext JS Library 1.1.1
17963  * Copyright(c) 2006-2007, Ext JS, LLC.
17964  *
17965  * Originally Released Under LGPL - original licence link has changed is not relivant.
17966  *
17967  * Fork - LGPL
17968  * <script type="text/javascript">
17969  */
17970
17971
17972
17973 /*
17974  * These classes are derivatives of the similarly named classes in the YUI Library.
17975  * The original license:
17976  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
17977  * Code licensed under the BSD License:
17978  * http://developer.yahoo.net/yui/license.txt
17979  */
17980
17981 (function() {
17982
17983 var Event=Roo.EventManager;
17984 var Dom=Roo.lib.Dom;
17985
17986 /**
17987  * @class Roo.dd.DragDrop
17988  * @extends Roo.util.Observable
17989  * Defines the interface and base operation of items that that can be
17990  * dragged or can be drop targets.  It was designed to be extended, overriding
17991  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
17992  * Up to three html elements can be associated with a DragDrop instance:
17993  * <ul>
17994  * <li>linked element: the element that is passed into the constructor.
17995  * This is the element which defines the boundaries for interaction with
17996  * other DragDrop objects.</li>
17997  * <li>handle element(s): The drag operation only occurs if the element that
17998  * was clicked matches a handle element.  By default this is the linked
17999  * element, but there are times that you will want only a portion of the
18000  * linked element to initiate the drag operation, and the setHandleElId()
18001  * method provides a way to define this.</li>
18002  * <li>drag element: this represents the element that would be moved along
18003  * with the cursor during a drag operation.  By default, this is the linked
18004  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18005  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18006  * </li>
18007  * </ul>
18008  * This class should not be instantiated until the onload event to ensure that
18009  * the associated elements are available.
18010  * The following would define a DragDrop obj that would interact with any
18011  * other DragDrop obj in the "group1" group:
18012  * <pre>
18013  *  dd = new Roo.dd.DragDrop("div1", "group1");
18014  * </pre>
18015  * Since none of the event handlers have been implemented, nothing would
18016  * actually happen if you were to run the code above.  Normally you would
18017  * override this class or one of the default implementations, but you can
18018  * also override the methods you want on an instance of the class...
18019  * <pre>
18020  *  dd.onDragDrop = function(e, id) {
18021  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18022  *  }
18023  * </pre>
18024  * @constructor
18025  * @param {String} id of the element that is linked to this instance
18026  * @param {String} sGroup the group of related DragDrop objects
18027  * @param {object} config an object containing configurable attributes
18028  *                Valid properties for DragDrop:
18029  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18030  */
18031 Roo.dd.DragDrop = function(id, sGroup, config) {
18032     if (id) {
18033         this.init(id, sGroup, config);
18034     }
18035     
18036 };
18037
18038 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18039
18040     /**
18041      * The id of the element associated with this object.  This is what we
18042      * refer to as the "linked element" because the size and position of
18043      * this element is used to determine when the drag and drop objects have
18044      * interacted.
18045      * @property id
18046      * @type String
18047      */
18048     id: null,
18049
18050     /**
18051      * Configuration attributes passed into the constructor
18052      * @property config
18053      * @type object
18054      */
18055     config: null,
18056
18057     /**
18058      * The id of the element that will be dragged.  By default this is same
18059      * as the linked element , but could be changed to another element. Ex:
18060      * Roo.dd.DDProxy
18061      * @property dragElId
18062      * @type String
18063      * @private
18064      */
18065     dragElId: null,
18066
18067     /**
18068      * the id of the element that initiates the drag operation.  By default
18069      * this is the linked element, but could be changed to be a child of this
18070      * element.  This lets us do things like only starting the drag when the
18071      * header element within the linked html element is clicked.
18072      * @property handleElId
18073      * @type String
18074      * @private
18075      */
18076     handleElId: null,
18077
18078     /**
18079      * An associative array of HTML tags that will be ignored if clicked.
18080      * @property invalidHandleTypes
18081      * @type {string: string}
18082      */
18083     invalidHandleTypes: null,
18084
18085     /**
18086      * An associative array of ids for elements that will be ignored if clicked
18087      * @property invalidHandleIds
18088      * @type {string: string}
18089      */
18090     invalidHandleIds: null,
18091
18092     /**
18093      * An indexted array of css class names for elements that will be ignored
18094      * if clicked.
18095      * @property invalidHandleClasses
18096      * @type string[]
18097      */
18098     invalidHandleClasses: null,
18099
18100     /**
18101      * The linked element's absolute X position at the time the drag was
18102      * started
18103      * @property startPageX
18104      * @type int
18105      * @private
18106      */
18107     startPageX: 0,
18108
18109     /**
18110      * The linked element's absolute X position at the time the drag was
18111      * started
18112      * @property startPageY
18113      * @type int
18114      * @private
18115      */
18116     startPageY: 0,
18117
18118     /**
18119      * The group defines a logical collection of DragDrop objects that are
18120      * related.  Instances only get events when interacting with other
18121      * DragDrop object in the same group.  This lets us define multiple
18122      * groups using a single DragDrop subclass if we want.
18123      * @property groups
18124      * @type {string: string}
18125      */
18126     groups: null,
18127
18128     /**
18129      * Individual drag/drop instances can be locked.  This will prevent
18130      * onmousedown start drag.
18131      * @property locked
18132      * @type boolean
18133      * @private
18134      */
18135     locked: false,
18136
18137     /**
18138      * Lock this instance
18139      * @method lock
18140      */
18141     lock: function() { this.locked = true; },
18142
18143     /**
18144      * Unlock this instace
18145      * @method unlock
18146      */
18147     unlock: function() { this.locked = false; },
18148
18149     /**
18150      * By default, all insances can be a drop target.  This can be disabled by
18151      * setting isTarget to false.
18152      * @method isTarget
18153      * @type boolean
18154      */
18155     isTarget: true,
18156
18157     /**
18158      * The padding configured for this drag and drop object for calculating
18159      * the drop zone intersection with this object.
18160      * @method padding
18161      * @type int[]
18162      */
18163     padding: null,
18164
18165     /**
18166      * Cached reference to the linked element
18167      * @property _domRef
18168      * @private
18169      */
18170     _domRef: null,
18171
18172     /**
18173      * Internal typeof flag
18174      * @property __ygDragDrop
18175      * @private
18176      */
18177     __ygDragDrop: true,
18178
18179     /**
18180      * Set to true when horizontal contraints are applied
18181      * @property constrainX
18182      * @type boolean
18183      * @private
18184      */
18185     constrainX: false,
18186
18187     /**
18188      * Set to true when vertical contraints are applied
18189      * @property constrainY
18190      * @type boolean
18191      * @private
18192      */
18193     constrainY: false,
18194
18195     /**
18196      * The left constraint
18197      * @property minX
18198      * @type int
18199      * @private
18200      */
18201     minX: 0,
18202
18203     /**
18204      * The right constraint
18205      * @property maxX
18206      * @type int
18207      * @private
18208      */
18209     maxX: 0,
18210
18211     /**
18212      * The up constraint
18213      * @property minY
18214      * @type int
18215      * @type int
18216      * @private
18217      */
18218     minY: 0,
18219
18220     /**
18221      * The down constraint
18222      * @property maxY
18223      * @type int
18224      * @private
18225      */
18226     maxY: 0,
18227
18228     /**
18229      * Maintain offsets when we resetconstraints.  Set to true when you want
18230      * the position of the element relative to its parent to stay the same
18231      * when the page changes
18232      *
18233      * @property maintainOffset
18234      * @type boolean
18235      */
18236     maintainOffset: false,
18237
18238     /**
18239      * Array of pixel locations the element will snap to if we specified a
18240      * horizontal graduation/interval.  This array is generated automatically
18241      * when you define a tick interval.
18242      * @property xTicks
18243      * @type int[]
18244      */
18245     xTicks: null,
18246
18247     /**
18248      * Array of pixel locations the element will snap to if we specified a
18249      * vertical graduation/interval.  This array is generated automatically
18250      * when you define a tick interval.
18251      * @property yTicks
18252      * @type int[]
18253      */
18254     yTicks: null,
18255
18256     /**
18257      * By default the drag and drop instance will only respond to the primary
18258      * button click (left button for a right-handed mouse).  Set to true to
18259      * allow drag and drop to start with any mouse click that is propogated
18260      * by the browser
18261      * @property primaryButtonOnly
18262      * @type boolean
18263      */
18264     primaryButtonOnly: true,
18265
18266     /**
18267      * The availabe property is false until the linked dom element is accessible.
18268      * @property available
18269      * @type boolean
18270      */
18271     available: false,
18272
18273     /**
18274      * By default, drags can only be initiated if the mousedown occurs in the
18275      * region the linked element is.  This is done in part to work around a
18276      * bug in some browsers that mis-report the mousedown if the previous
18277      * mouseup happened outside of the window.  This property is set to true
18278      * if outer handles are defined.
18279      *
18280      * @property hasOuterHandles
18281      * @type boolean
18282      * @default false
18283      */
18284     hasOuterHandles: false,
18285
18286     /**
18287      * Code that executes immediately before the startDrag event
18288      * @method b4StartDrag
18289      * @private
18290      */
18291     b4StartDrag: function(x, y) { },
18292
18293     /**
18294      * Abstract method called after a drag/drop object is clicked
18295      * and the drag or mousedown time thresholds have beeen met.
18296      * @method startDrag
18297      * @param {int} X click location
18298      * @param {int} Y click location
18299      */
18300     startDrag: function(x, y) { /* override this */ },
18301
18302     /**
18303      * Code that executes immediately before the onDrag event
18304      * @method b4Drag
18305      * @private
18306      */
18307     b4Drag: function(e) { },
18308
18309     /**
18310      * Abstract method called during the onMouseMove event while dragging an
18311      * object.
18312      * @method onDrag
18313      * @param {Event} e the mousemove event
18314      */
18315     onDrag: function(e) { /* override this */ },
18316
18317     /**
18318      * Abstract method called when this element fist begins hovering over
18319      * another DragDrop obj
18320      * @method onDragEnter
18321      * @param {Event} e the mousemove event
18322      * @param {String|DragDrop[]} id In POINT mode, the element
18323      * id this is hovering over.  In INTERSECT mode, an array of one or more
18324      * dragdrop items being hovered over.
18325      */
18326     onDragEnter: function(e, id) { /* override this */ },
18327
18328     /**
18329      * Code that executes immediately before the onDragOver event
18330      * @method b4DragOver
18331      * @private
18332      */
18333     b4DragOver: function(e) { },
18334
18335     /**
18336      * Abstract method called when this element is hovering over another
18337      * DragDrop obj
18338      * @method onDragOver
18339      * @param {Event} e the mousemove event
18340      * @param {String|DragDrop[]} id In POINT mode, the element
18341      * id this is hovering over.  In INTERSECT mode, an array of dd items
18342      * being hovered over.
18343      */
18344     onDragOver: function(e, id) { /* override this */ },
18345
18346     /**
18347      * Code that executes immediately before the onDragOut event
18348      * @method b4DragOut
18349      * @private
18350      */
18351     b4DragOut: function(e) { },
18352
18353     /**
18354      * Abstract method called when we are no longer hovering over an element
18355      * @method onDragOut
18356      * @param {Event} e the mousemove event
18357      * @param {String|DragDrop[]} id In POINT mode, the element
18358      * id this was hovering over.  In INTERSECT mode, an array of dd items
18359      * that the mouse is no longer over.
18360      */
18361     onDragOut: function(e, id) { /* override this */ },
18362
18363     /**
18364      * Code that executes immediately before the onDragDrop event
18365      * @method b4DragDrop
18366      * @private
18367      */
18368     b4DragDrop: function(e) { },
18369
18370     /**
18371      * Abstract method called when this item is dropped on another DragDrop
18372      * obj
18373      * @method onDragDrop
18374      * @param {Event} e the mouseup event
18375      * @param {String|DragDrop[]} id In POINT mode, the element
18376      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18377      * was dropped on.
18378      */
18379     onDragDrop: function(e, id) { /* override this */ },
18380
18381     /**
18382      * Abstract method called when this item is dropped on an area with no
18383      * drop target
18384      * @method onInvalidDrop
18385      * @param {Event} e the mouseup event
18386      */
18387     onInvalidDrop: function(e) { /* override this */ },
18388
18389     /**
18390      * Code that executes immediately before the endDrag event
18391      * @method b4EndDrag
18392      * @private
18393      */
18394     b4EndDrag: function(e) { },
18395
18396     /**
18397      * Fired when we are done dragging the object
18398      * @method endDrag
18399      * @param {Event} e the mouseup event
18400      */
18401     endDrag: function(e) { /* override this */ },
18402
18403     /**
18404      * Code executed immediately before the onMouseDown event
18405      * @method b4MouseDown
18406      * @param {Event} e the mousedown event
18407      * @private
18408      */
18409     b4MouseDown: function(e) {  },
18410
18411     /**
18412      * Event handler that fires when a drag/drop obj gets a mousedown
18413      * @method onMouseDown
18414      * @param {Event} e the mousedown event
18415      */
18416     onMouseDown: function(e) { /* override this */ },
18417
18418     /**
18419      * Event handler that fires when a drag/drop obj gets a mouseup
18420      * @method onMouseUp
18421      * @param {Event} e the mouseup event
18422      */
18423     onMouseUp: function(e) { /* override this */ },
18424
18425     /**
18426      * Override the onAvailable method to do what is needed after the initial
18427      * position was determined.
18428      * @method onAvailable
18429      */
18430     onAvailable: function () {
18431     },
18432
18433     /*
18434      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18435      * @type Object
18436      */
18437     defaultPadding : {left:0, right:0, top:0, bottom:0},
18438
18439     /*
18440      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18441  *
18442  * Usage:
18443  <pre><code>
18444  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18445                 { dragElId: "existingProxyDiv" });
18446  dd.startDrag = function(){
18447      this.constrainTo("parent-id");
18448  };
18449  </code></pre>
18450  * Or you can initalize it using the {@link Roo.Element} object:
18451  <pre><code>
18452  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18453      startDrag : function(){
18454          this.constrainTo("parent-id");
18455      }
18456  });
18457  </code></pre>
18458      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18459      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18460      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18461      * an object containing the sides to pad. For example: {right:10, bottom:10}
18462      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18463      */
18464     constrainTo : function(constrainTo, pad, inContent){
18465         if(typeof pad == "number"){
18466             pad = {left: pad, right:pad, top:pad, bottom:pad};
18467         }
18468         pad = pad || this.defaultPadding;
18469         var b = Roo.get(this.getEl()).getBox();
18470         var ce = Roo.get(constrainTo);
18471         var s = ce.getScroll();
18472         var c, cd = ce.dom;
18473         if(cd == document.body){
18474             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18475         }else{
18476             xy = ce.getXY();
18477             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18478         }
18479
18480
18481         var topSpace = b.y - c.y;
18482         var leftSpace = b.x - c.x;
18483
18484         this.resetConstraints();
18485         this.setXConstraint(leftSpace - (pad.left||0), // left
18486                 c.width - leftSpace - b.width - (pad.right||0) //right
18487         );
18488         this.setYConstraint(topSpace - (pad.top||0), //top
18489                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18490         );
18491     },
18492
18493     /**
18494      * Returns a reference to the linked element
18495      * @method getEl
18496      * @return {HTMLElement} the html element
18497      */
18498     getEl: function() {
18499         if (!this._domRef) {
18500             this._domRef = Roo.getDom(this.id);
18501         }
18502
18503         return this._domRef;
18504     },
18505
18506     /**
18507      * Returns a reference to the actual element to drag.  By default this is
18508      * the same as the html element, but it can be assigned to another
18509      * element. An example of this can be found in Roo.dd.DDProxy
18510      * @method getDragEl
18511      * @return {HTMLElement} the html element
18512      */
18513     getDragEl: function() {
18514         return Roo.getDom(this.dragElId);
18515     },
18516
18517     /**
18518      * Sets up the DragDrop object.  Must be called in the constructor of any
18519      * Roo.dd.DragDrop subclass
18520      * @method init
18521      * @param id the id of the linked element
18522      * @param {String} sGroup the group of related items
18523      * @param {object} config configuration attributes
18524      */
18525     init: function(id, sGroup, config) {
18526         this.initTarget(id, sGroup, config);
18527         if (!Roo.isTouch) {
18528             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18529         }
18530         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18531         // Event.on(this.id, "selectstart", Event.preventDefault);
18532     },
18533
18534     /**
18535      * Initializes Targeting functionality only... the object does not
18536      * get a mousedown handler.
18537      * @method initTarget
18538      * @param id the id of the linked element
18539      * @param {String} sGroup the group of related items
18540      * @param {object} config configuration attributes
18541      */
18542     initTarget: function(id, sGroup, config) {
18543
18544         // configuration attributes
18545         this.config = config || {};
18546
18547         // create a local reference to the drag and drop manager
18548         this.DDM = Roo.dd.DDM;
18549         // initialize the groups array
18550         this.groups = {};
18551
18552         // assume that we have an element reference instead of an id if the
18553         // parameter is not a string
18554         if (typeof id !== "string") {
18555             id = Roo.id(id);
18556         }
18557
18558         // set the id
18559         this.id = id;
18560
18561         // add to an interaction group
18562         this.addToGroup((sGroup) ? sGroup : "default");
18563
18564         // We don't want to register this as the handle with the manager
18565         // so we just set the id rather than calling the setter.
18566         this.handleElId = id;
18567
18568         // the linked element is the element that gets dragged by default
18569         this.setDragElId(id);
18570
18571         // by default, clicked anchors will not start drag operations.
18572         this.invalidHandleTypes = { A: "A" };
18573         this.invalidHandleIds = {};
18574         this.invalidHandleClasses = [];
18575
18576         this.applyConfig();
18577
18578         this.handleOnAvailable();
18579     },
18580
18581     /**
18582      * Applies the configuration parameters that were passed into the constructor.
18583      * This is supposed to happen at each level through the inheritance chain.  So
18584      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18585      * DragDrop in order to get all of the parameters that are available in
18586      * each object.
18587      * @method applyConfig
18588      */
18589     applyConfig: function() {
18590
18591         // configurable properties:
18592         //    padding, isTarget, maintainOffset, primaryButtonOnly
18593         this.padding           = this.config.padding || [0, 0, 0, 0];
18594         this.isTarget          = (this.config.isTarget !== false);
18595         this.maintainOffset    = (this.config.maintainOffset);
18596         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18597
18598     },
18599
18600     /**
18601      * Executed when the linked element is available
18602      * @method handleOnAvailable
18603      * @private
18604      */
18605     handleOnAvailable: function() {
18606         this.available = true;
18607         this.resetConstraints();
18608         this.onAvailable();
18609     },
18610
18611      /**
18612      * Configures the padding for the target zone in px.  Effectively expands
18613      * (or reduces) the virtual object size for targeting calculations.
18614      * Supports css-style shorthand; if only one parameter is passed, all sides
18615      * will have that padding, and if only two are passed, the top and bottom
18616      * will have the first param, the left and right the second.
18617      * @method setPadding
18618      * @param {int} iTop    Top pad
18619      * @param {int} iRight  Right pad
18620      * @param {int} iBot    Bot pad
18621      * @param {int} iLeft   Left pad
18622      */
18623     setPadding: function(iTop, iRight, iBot, iLeft) {
18624         // this.padding = [iLeft, iRight, iTop, iBot];
18625         if (!iRight && 0 !== iRight) {
18626             this.padding = [iTop, iTop, iTop, iTop];
18627         } else if (!iBot && 0 !== iBot) {
18628             this.padding = [iTop, iRight, iTop, iRight];
18629         } else {
18630             this.padding = [iTop, iRight, iBot, iLeft];
18631         }
18632     },
18633
18634     /**
18635      * Stores the initial placement of the linked element.
18636      * @method setInitialPosition
18637      * @param {int} diffX   the X offset, default 0
18638      * @param {int} diffY   the Y offset, default 0
18639      */
18640     setInitPosition: function(diffX, diffY) {
18641         var el = this.getEl();
18642
18643         if (!this.DDM.verifyEl(el)) {
18644             return;
18645         }
18646
18647         var dx = diffX || 0;
18648         var dy = diffY || 0;
18649
18650         var p = Dom.getXY( el );
18651
18652         this.initPageX = p[0] - dx;
18653         this.initPageY = p[1] - dy;
18654
18655         this.lastPageX = p[0];
18656         this.lastPageY = p[1];
18657
18658
18659         this.setStartPosition(p);
18660     },
18661
18662     /**
18663      * Sets the start position of the element.  This is set when the obj
18664      * is initialized, the reset when a drag is started.
18665      * @method setStartPosition
18666      * @param pos current position (from previous lookup)
18667      * @private
18668      */
18669     setStartPosition: function(pos) {
18670         var p = pos || Dom.getXY( this.getEl() );
18671         this.deltaSetXY = null;
18672
18673         this.startPageX = p[0];
18674         this.startPageY = p[1];
18675     },
18676
18677     /**
18678      * Add this instance to a group of related drag/drop objects.  All
18679      * instances belong to at least one group, and can belong to as many
18680      * groups as needed.
18681      * @method addToGroup
18682      * @param sGroup {string} the name of the group
18683      */
18684     addToGroup: function(sGroup) {
18685         this.groups[sGroup] = true;
18686         this.DDM.regDragDrop(this, sGroup);
18687     },
18688
18689     /**
18690      * Remove's this instance from the supplied interaction group
18691      * @method removeFromGroup
18692      * @param {string}  sGroup  The group to drop
18693      */
18694     removeFromGroup: function(sGroup) {
18695         if (this.groups[sGroup]) {
18696             delete this.groups[sGroup];
18697         }
18698
18699         this.DDM.removeDDFromGroup(this, sGroup);
18700     },
18701
18702     /**
18703      * Allows you to specify that an element other than the linked element
18704      * will be moved with the cursor during a drag
18705      * @method setDragElId
18706      * @param id {string} the id of the element that will be used to initiate the drag
18707      */
18708     setDragElId: function(id) {
18709         this.dragElId = id;
18710     },
18711
18712     /**
18713      * Allows you to specify a child of the linked element that should be
18714      * used to initiate the drag operation.  An example of this would be if
18715      * you have a content div with text and links.  Clicking anywhere in the
18716      * content area would normally start the drag operation.  Use this method
18717      * to specify that an element inside of the content div is the element
18718      * that starts the drag operation.
18719      * @method setHandleElId
18720      * @param id {string} the id of the element that will be used to
18721      * initiate the drag.
18722      */
18723     setHandleElId: function(id) {
18724         if (typeof id !== "string") {
18725             id = Roo.id(id);
18726         }
18727         this.handleElId = id;
18728         this.DDM.regHandle(this.id, id);
18729     },
18730
18731     /**
18732      * Allows you to set an element outside of the linked element as a drag
18733      * handle
18734      * @method setOuterHandleElId
18735      * @param id the id of the element that will be used to initiate the drag
18736      */
18737     setOuterHandleElId: function(id) {
18738         if (typeof id !== "string") {
18739             id = Roo.id(id);
18740         }
18741         Event.on(id, "mousedown",
18742                 this.handleMouseDown, this);
18743         this.setHandleElId(id);
18744
18745         this.hasOuterHandles = true;
18746     },
18747
18748     /**
18749      * Remove all drag and drop hooks for this element
18750      * @method unreg
18751      */
18752     unreg: function() {
18753         Event.un(this.id, "mousedown",
18754                 this.handleMouseDown);
18755         Event.un(this.id, "touchstart",
18756                 this.handleMouseDown);
18757         this._domRef = null;
18758         this.DDM._remove(this);
18759     },
18760
18761     destroy : function(){
18762         this.unreg();
18763     },
18764
18765     /**
18766      * Returns true if this instance is locked, or the drag drop mgr is locked
18767      * (meaning that all drag/drop is disabled on the page.)
18768      * @method isLocked
18769      * @return {boolean} true if this obj or all drag/drop is locked, else
18770      * false
18771      */
18772     isLocked: function() {
18773         return (this.DDM.isLocked() || this.locked);
18774     },
18775
18776     /**
18777      * Fired when this object is clicked
18778      * @method handleMouseDown
18779      * @param {Event} e
18780      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18781      * @private
18782      */
18783     handleMouseDown: function(e, oDD){
18784      
18785         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18786             //Roo.log('not touch/ button !=0');
18787             return;
18788         }
18789         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18790             return; // double touch..
18791         }
18792         
18793
18794         if (this.isLocked()) {
18795             //Roo.log('locked');
18796             return;
18797         }
18798
18799         this.DDM.refreshCache(this.groups);
18800 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18801         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18802         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18803             //Roo.log('no outer handes or not over target');
18804                 // do nothing.
18805         } else {
18806 //            Roo.log('check validator');
18807             if (this.clickValidator(e)) {
18808 //                Roo.log('validate success');
18809                 // set the initial element position
18810                 this.setStartPosition();
18811
18812
18813                 this.b4MouseDown(e);
18814                 this.onMouseDown(e);
18815
18816                 this.DDM.handleMouseDown(e, this);
18817
18818                 this.DDM.stopEvent(e);
18819             } else {
18820
18821
18822             }
18823         }
18824     },
18825
18826     clickValidator: function(e) {
18827         var target = e.getTarget();
18828         return ( this.isValidHandleChild(target) &&
18829                     (this.id == this.handleElId ||
18830                         this.DDM.handleWasClicked(target, this.id)) );
18831     },
18832
18833     /**
18834      * Allows you to specify a tag name that should not start a drag operation
18835      * when clicked.  This is designed to facilitate embedding links within a
18836      * drag handle that do something other than start the drag.
18837      * @method addInvalidHandleType
18838      * @param {string} tagName the type of element to exclude
18839      */
18840     addInvalidHandleType: function(tagName) {
18841         var type = tagName.toUpperCase();
18842         this.invalidHandleTypes[type] = type;
18843     },
18844
18845     /**
18846      * Lets you to specify an element id for a child of a drag handle
18847      * that should not initiate a drag
18848      * @method addInvalidHandleId
18849      * @param {string} id the element id of the element you wish to ignore
18850      */
18851     addInvalidHandleId: function(id) {
18852         if (typeof id !== "string") {
18853             id = Roo.id(id);
18854         }
18855         this.invalidHandleIds[id] = id;
18856     },
18857
18858     /**
18859      * Lets you specify a css class of elements that will not initiate a drag
18860      * @method addInvalidHandleClass
18861      * @param {string} cssClass the class of the elements you wish to ignore
18862      */
18863     addInvalidHandleClass: function(cssClass) {
18864         this.invalidHandleClasses.push(cssClass);
18865     },
18866
18867     /**
18868      * Unsets an excluded tag name set by addInvalidHandleType
18869      * @method removeInvalidHandleType
18870      * @param {string} tagName the type of element to unexclude
18871      */
18872     removeInvalidHandleType: function(tagName) {
18873         var type = tagName.toUpperCase();
18874         // this.invalidHandleTypes[type] = null;
18875         delete this.invalidHandleTypes[type];
18876     },
18877
18878     /**
18879      * Unsets an invalid handle id
18880      * @method removeInvalidHandleId
18881      * @param {string} id the id of the element to re-enable
18882      */
18883     removeInvalidHandleId: function(id) {
18884         if (typeof id !== "string") {
18885             id = Roo.id(id);
18886         }
18887         delete this.invalidHandleIds[id];
18888     },
18889
18890     /**
18891      * Unsets an invalid css class
18892      * @method removeInvalidHandleClass
18893      * @param {string} cssClass the class of the element(s) you wish to
18894      * re-enable
18895      */
18896     removeInvalidHandleClass: function(cssClass) {
18897         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
18898             if (this.invalidHandleClasses[i] == cssClass) {
18899                 delete this.invalidHandleClasses[i];
18900             }
18901         }
18902     },
18903
18904     /**
18905      * Checks the tag exclusion list to see if this click should be ignored
18906      * @method isValidHandleChild
18907      * @param {HTMLElement} node the HTMLElement to evaluate
18908      * @return {boolean} true if this is a valid tag type, false if not
18909      */
18910     isValidHandleChild: function(node) {
18911
18912         var valid = true;
18913         // var n = (node.nodeName == "#text") ? node.parentNode : node;
18914         var nodeName;
18915         try {
18916             nodeName = node.nodeName.toUpperCase();
18917         } catch(e) {
18918             nodeName = node.nodeName;
18919         }
18920         valid = valid && !this.invalidHandleTypes[nodeName];
18921         valid = valid && !this.invalidHandleIds[node.id];
18922
18923         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
18924             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
18925         }
18926
18927
18928         return valid;
18929
18930     },
18931
18932     /**
18933      * Create the array of horizontal tick marks if an interval was specified
18934      * in setXConstraint().
18935      * @method setXTicks
18936      * @private
18937      */
18938     setXTicks: function(iStartX, iTickSize) {
18939         this.xTicks = [];
18940         this.xTickSize = iTickSize;
18941
18942         var tickMap = {};
18943
18944         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
18945             if (!tickMap[i]) {
18946                 this.xTicks[this.xTicks.length] = i;
18947                 tickMap[i] = true;
18948             }
18949         }
18950
18951         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
18952             if (!tickMap[i]) {
18953                 this.xTicks[this.xTicks.length] = i;
18954                 tickMap[i] = true;
18955             }
18956         }
18957
18958         this.xTicks.sort(this.DDM.numericSort) ;
18959     },
18960
18961     /**
18962      * Create the array of vertical tick marks if an interval was specified in
18963      * setYConstraint().
18964      * @method setYTicks
18965      * @private
18966      */
18967     setYTicks: function(iStartY, iTickSize) {
18968         this.yTicks = [];
18969         this.yTickSize = iTickSize;
18970
18971         var tickMap = {};
18972
18973         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
18974             if (!tickMap[i]) {
18975                 this.yTicks[this.yTicks.length] = i;
18976                 tickMap[i] = true;
18977             }
18978         }
18979
18980         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
18981             if (!tickMap[i]) {
18982                 this.yTicks[this.yTicks.length] = i;
18983                 tickMap[i] = true;
18984             }
18985         }
18986
18987         this.yTicks.sort(this.DDM.numericSort) ;
18988     },
18989
18990     /**
18991      * By default, the element can be dragged any place on the screen.  Use
18992      * this method to limit the horizontal travel of the element.  Pass in
18993      * 0,0 for the parameters if you want to lock the drag to the y axis.
18994      * @method setXConstraint
18995      * @param {int} iLeft the number of pixels the element can move to the left
18996      * @param {int} iRight the number of pixels the element can move to the
18997      * right
18998      * @param {int} iTickSize optional parameter for specifying that the
18999      * element
19000      * should move iTickSize pixels at a time.
19001      */
19002     setXConstraint: function(iLeft, iRight, iTickSize) {
19003         this.leftConstraint = iLeft;
19004         this.rightConstraint = iRight;
19005
19006         this.minX = this.initPageX - iLeft;
19007         this.maxX = this.initPageX + iRight;
19008         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19009
19010         this.constrainX = true;
19011     },
19012
19013     /**
19014      * Clears any constraints applied to this instance.  Also clears ticks
19015      * since they can't exist independent of a constraint at this time.
19016      * @method clearConstraints
19017      */
19018     clearConstraints: function() {
19019         this.constrainX = false;
19020         this.constrainY = false;
19021         this.clearTicks();
19022     },
19023
19024     /**
19025      * Clears any tick interval defined for this instance
19026      * @method clearTicks
19027      */
19028     clearTicks: function() {
19029         this.xTicks = null;
19030         this.yTicks = null;
19031         this.xTickSize = 0;
19032         this.yTickSize = 0;
19033     },
19034
19035     /**
19036      * By default, the element can be dragged any place on the screen.  Set
19037      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19038      * parameters if you want to lock the drag to the x axis.
19039      * @method setYConstraint
19040      * @param {int} iUp the number of pixels the element can move up
19041      * @param {int} iDown the number of pixels the element can move down
19042      * @param {int} iTickSize optional parameter for specifying that the
19043      * element should move iTickSize pixels at a time.
19044      */
19045     setYConstraint: function(iUp, iDown, iTickSize) {
19046         this.topConstraint = iUp;
19047         this.bottomConstraint = iDown;
19048
19049         this.minY = this.initPageY - iUp;
19050         this.maxY = this.initPageY + iDown;
19051         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19052
19053         this.constrainY = true;
19054
19055     },
19056
19057     /**
19058      * resetConstraints must be called if you manually reposition a dd element.
19059      * @method resetConstraints
19060      * @param {boolean} maintainOffset
19061      */
19062     resetConstraints: function() {
19063
19064
19065         // Maintain offsets if necessary
19066         if (this.initPageX || this.initPageX === 0) {
19067             // figure out how much this thing has moved
19068             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19069             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19070
19071             this.setInitPosition(dx, dy);
19072
19073         // This is the first time we have detected the element's position
19074         } else {
19075             this.setInitPosition();
19076         }
19077
19078         if (this.constrainX) {
19079             this.setXConstraint( this.leftConstraint,
19080                                  this.rightConstraint,
19081                                  this.xTickSize        );
19082         }
19083
19084         if (this.constrainY) {
19085             this.setYConstraint( this.topConstraint,
19086                                  this.bottomConstraint,
19087                                  this.yTickSize         );
19088         }
19089     },
19090
19091     /**
19092      * Normally the drag element is moved pixel by pixel, but we can specify
19093      * that it move a number of pixels at a time.  This method resolves the
19094      * location when we have it set up like this.
19095      * @method getTick
19096      * @param {int} val where we want to place the object
19097      * @param {int[]} tickArray sorted array of valid points
19098      * @return {int} the closest tick
19099      * @private
19100      */
19101     getTick: function(val, tickArray) {
19102
19103         if (!tickArray) {
19104             // If tick interval is not defined, it is effectively 1 pixel,
19105             // so we return the value passed to us.
19106             return val;
19107         } else if (tickArray[0] >= val) {
19108             // The value is lower than the first tick, so we return the first
19109             // tick.
19110             return tickArray[0];
19111         } else {
19112             for (var i=0, len=tickArray.length; i<len; ++i) {
19113                 var next = i + 1;
19114                 if (tickArray[next] && tickArray[next] >= val) {
19115                     var diff1 = val - tickArray[i];
19116                     var diff2 = tickArray[next] - val;
19117                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19118                 }
19119             }
19120
19121             // The value is larger than the last tick, so we return the last
19122             // tick.
19123             return tickArray[tickArray.length - 1];
19124         }
19125     },
19126
19127     /**
19128      * toString method
19129      * @method toString
19130      * @return {string} string representation of the dd obj
19131      */
19132     toString: function() {
19133         return ("DragDrop " + this.id);
19134     }
19135
19136 });
19137
19138 })();
19139 /*
19140  * Based on:
19141  * Ext JS Library 1.1.1
19142  * Copyright(c) 2006-2007, Ext JS, LLC.
19143  *
19144  * Originally Released Under LGPL - original licence link has changed is not relivant.
19145  *
19146  * Fork - LGPL
19147  * <script type="text/javascript">
19148  */
19149
19150
19151 /**
19152  * The drag and drop utility provides a framework for building drag and drop
19153  * applications.  In addition to enabling drag and drop for specific elements,
19154  * the drag and drop elements are tracked by the manager class, and the
19155  * interactions between the various elements are tracked during the drag and
19156  * the implementing code is notified about these important moments.
19157  */
19158
19159 // Only load the library once.  Rewriting the manager class would orphan
19160 // existing drag and drop instances.
19161 if (!Roo.dd.DragDropMgr) {
19162
19163 /**
19164  * @class Roo.dd.DragDropMgr
19165  * DragDropMgr is a singleton that tracks the element interaction for
19166  * all DragDrop items in the window.  Generally, you will not call
19167  * this class directly, but it does have helper methods that could
19168  * be useful in your DragDrop implementations.
19169  * @singleton
19170  */
19171 Roo.dd.DragDropMgr = function() {
19172
19173     var Event = Roo.EventManager;
19174
19175     return {
19176
19177         /**
19178          * Two dimensional Array of registered DragDrop objects.  The first
19179          * dimension is the DragDrop item group, the second the DragDrop
19180          * object.
19181          * @property ids
19182          * @type {string: string}
19183          * @private
19184          * @static
19185          */
19186         ids: {},
19187
19188         /**
19189          * Array of element ids defined as drag handles.  Used to determine
19190          * if the element that generated the mousedown event is actually the
19191          * handle and not the html element itself.
19192          * @property handleIds
19193          * @type {string: string}
19194          * @private
19195          * @static
19196          */
19197         handleIds: {},
19198
19199         /**
19200          * the DragDrop object that is currently being dragged
19201          * @property dragCurrent
19202          * @type DragDrop
19203          * @private
19204          * @static
19205          **/
19206         dragCurrent: null,
19207
19208         /**
19209          * the DragDrop object(s) that are being hovered over
19210          * @property dragOvers
19211          * @type Array
19212          * @private
19213          * @static
19214          */
19215         dragOvers: {},
19216
19217         /**
19218          * the X distance between the cursor and the object being dragged
19219          * @property deltaX
19220          * @type int
19221          * @private
19222          * @static
19223          */
19224         deltaX: 0,
19225
19226         /**
19227          * the Y distance between the cursor and the object being dragged
19228          * @property deltaY
19229          * @type int
19230          * @private
19231          * @static
19232          */
19233         deltaY: 0,
19234
19235         /**
19236          * Flag to determine if we should prevent the default behavior of the
19237          * events we define. By default this is true, but this can be set to
19238          * false if you need the default behavior (not recommended)
19239          * @property preventDefault
19240          * @type boolean
19241          * @static
19242          */
19243         preventDefault: true,
19244
19245         /**
19246          * Flag to determine if we should stop the propagation of the events
19247          * we generate. This is true by default but you may want to set it to
19248          * false if the html element contains other features that require the
19249          * mouse click.
19250          * @property stopPropagation
19251          * @type boolean
19252          * @static
19253          */
19254         stopPropagation: true,
19255
19256         /**
19257          * Internal flag that is set to true when drag and drop has been
19258          * intialized
19259          * @property initialized
19260          * @private
19261          * @static
19262          */
19263         initalized: false,
19264
19265         /**
19266          * All drag and drop can be disabled.
19267          * @property locked
19268          * @private
19269          * @static
19270          */
19271         locked: false,
19272
19273         /**
19274          * Called the first time an element is registered.
19275          * @method init
19276          * @private
19277          * @static
19278          */
19279         init: function() {
19280             this.initialized = true;
19281         },
19282
19283         /**
19284          * In point mode, drag and drop interaction is defined by the
19285          * location of the cursor during the drag/drop
19286          * @property POINT
19287          * @type int
19288          * @static
19289          */
19290         POINT: 0,
19291
19292         /**
19293          * In intersect mode, drag and drop interactio nis defined by the
19294          * overlap of two or more drag and drop objects.
19295          * @property INTERSECT
19296          * @type int
19297          * @static
19298          */
19299         INTERSECT: 1,
19300
19301         /**
19302          * The current drag and drop mode.  Default: POINT
19303          * @property mode
19304          * @type int
19305          * @static
19306          */
19307         mode: 0,
19308
19309         /**
19310          * Runs method on all drag and drop objects
19311          * @method _execOnAll
19312          * @private
19313          * @static
19314          */
19315         _execOnAll: function(sMethod, args) {
19316             for (var i in this.ids) {
19317                 for (var j in this.ids[i]) {
19318                     var oDD = this.ids[i][j];
19319                     if (! this.isTypeOfDD(oDD)) {
19320                         continue;
19321                     }
19322                     oDD[sMethod].apply(oDD, args);
19323                 }
19324             }
19325         },
19326
19327         /**
19328          * Drag and drop initialization.  Sets up the global event handlers
19329          * @method _onLoad
19330          * @private
19331          * @static
19332          */
19333         _onLoad: function() {
19334
19335             this.init();
19336
19337             if (!Roo.isTouch) {
19338                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19339                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19340             }
19341             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19342             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19343             
19344             Event.on(window,   "unload",    this._onUnload, this, true);
19345             Event.on(window,   "resize",    this._onResize, this, true);
19346             // Event.on(window,   "mouseout",    this._test);
19347
19348         },
19349
19350         /**
19351          * Reset constraints on all drag and drop objs
19352          * @method _onResize
19353          * @private
19354          * @static
19355          */
19356         _onResize: function(e) {
19357             this._execOnAll("resetConstraints", []);
19358         },
19359
19360         /**
19361          * Lock all drag and drop functionality
19362          * @method lock
19363          * @static
19364          */
19365         lock: function() { this.locked = true; },
19366
19367         /**
19368          * Unlock all drag and drop functionality
19369          * @method unlock
19370          * @static
19371          */
19372         unlock: function() { this.locked = false; },
19373
19374         /**
19375          * Is drag and drop locked?
19376          * @method isLocked
19377          * @return {boolean} True if drag and drop is locked, false otherwise.
19378          * @static
19379          */
19380         isLocked: function() { return this.locked; },
19381
19382         /**
19383          * Location cache that is set for all drag drop objects when a drag is
19384          * initiated, cleared when the drag is finished.
19385          * @property locationCache
19386          * @private
19387          * @static
19388          */
19389         locationCache: {},
19390
19391         /**
19392          * Set useCache to false if you want to force object the lookup of each
19393          * drag and drop linked element constantly during a drag.
19394          * @property useCache
19395          * @type boolean
19396          * @static
19397          */
19398         useCache: true,
19399
19400         /**
19401          * The number of pixels that the mouse needs to move after the
19402          * mousedown before the drag is initiated.  Default=3;
19403          * @property clickPixelThresh
19404          * @type int
19405          * @static
19406          */
19407         clickPixelThresh: 3,
19408
19409         /**
19410          * The number of milliseconds after the mousedown event to initiate the
19411          * drag if we don't get a mouseup event. Default=1000
19412          * @property clickTimeThresh
19413          * @type int
19414          * @static
19415          */
19416         clickTimeThresh: 350,
19417
19418         /**
19419          * Flag that indicates that either the drag pixel threshold or the
19420          * mousdown time threshold has been met
19421          * @property dragThreshMet
19422          * @type boolean
19423          * @private
19424          * @static
19425          */
19426         dragThreshMet: false,
19427
19428         /**
19429          * Timeout used for the click time threshold
19430          * @property clickTimeout
19431          * @type Object
19432          * @private
19433          * @static
19434          */
19435         clickTimeout: null,
19436
19437         /**
19438          * The X position of the mousedown event stored for later use when a
19439          * drag threshold is met.
19440          * @property startX
19441          * @type int
19442          * @private
19443          * @static
19444          */
19445         startX: 0,
19446
19447         /**
19448          * The Y position of the mousedown event stored for later use when a
19449          * drag threshold is met.
19450          * @property startY
19451          * @type int
19452          * @private
19453          * @static
19454          */
19455         startY: 0,
19456
19457         /**
19458          * Each DragDrop instance must be registered with the DragDropMgr.
19459          * This is executed in DragDrop.init()
19460          * @method regDragDrop
19461          * @param {DragDrop} oDD the DragDrop object to register
19462          * @param {String} sGroup the name of the group this element belongs to
19463          * @static
19464          */
19465         regDragDrop: function(oDD, sGroup) {
19466             if (!this.initialized) { this.init(); }
19467
19468             if (!this.ids[sGroup]) {
19469                 this.ids[sGroup] = {};
19470             }
19471             this.ids[sGroup][oDD.id] = oDD;
19472         },
19473
19474         /**
19475          * Removes the supplied dd instance from the supplied group. Executed
19476          * by DragDrop.removeFromGroup, so don't call this function directly.
19477          * @method removeDDFromGroup
19478          * @private
19479          * @static
19480          */
19481         removeDDFromGroup: function(oDD, sGroup) {
19482             if (!this.ids[sGroup]) {
19483                 this.ids[sGroup] = {};
19484             }
19485
19486             var obj = this.ids[sGroup];
19487             if (obj && obj[oDD.id]) {
19488                 delete obj[oDD.id];
19489             }
19490         },
19491
19492         /**
19493          * Unregisters a drag and drop item.  This is executed in
19494          * DragDrop.unreg, use that method instead of calling this directly.
19495          * @method _remove
19496          * @private
19497          * @static
19498          */
19499         _remove: function(oDD) {
19500             for (var g in oDD.groups) {
19501                 if (g && this.ids[g][oDD.id]) {
19502                     delete this.ids[g][oDD.id];
19503                 }
19504             }
19505             delete this.handleIds[oDD.id];
19506         },
19507
19508         /**
19509          * Each DragDrop handle element must be registered.  This is done
19510          * automatically when executing DragDrop.setHandleElId()
19511          * @method regHandle
19512          * @param {String} sDDId the DragDrop id this element is a handle for
19513          * @param {String} sHandleId the id of the element that is the drag
19514          * handle
19515          * @static
19516          */
19517         regHandle: function(sDDId, sHandleId) {
19518             if (!this.handleIds[sDDId]) {
19519                 this.handleIds[sDDId] = {};
19520             }
19521             this.handleIds[sDDId][sHandleId] = sHandleId;
19522         },
19523
19524         /**
19525          * Utility function to determine if a given element has been
19526          * registered as a drag drop item.
19527          * @method isDragDrop
19528          * @param {String} id the element id to check
19529          * @return {boolean} true if this element is a DragDrop item,
19530          * false otherwise
19531          * @static
19532          */
19533         isDragDrop: function(id) {
19534             return ( this.getDDById(id) ) ? true : false;
19535         },
19536
19537         /**
19538          * Returns the drag and drop instances that are in all groups the
19539          * passed in instance belongs to.
19540          * @method getRelated
19541          * @param {DragDrop} p_oDD the obj to get related data for
19542          * @param {boolean} bTargetsOnly if true, only return targetable objs
19543          * @return {DragDrop[]} the related instances
19544          * @static
19545          */
19546         getRelated: function(p_oDD, bTargetsOnly) {
19547             var oDDs = [];
19548             for (var i in p_oDD.groups) {
19549                 for (j in this.ids[i]) {
19550                     var dd = this.ids[i][j];
19551                     if (! this.isTypeOfDD(dd)) {
19552                         continue;
19553                     }
19554                     if (!bTargetsOnly || dd.isTarget) {
19555                         oDDs[oDDs.length] = dd;
19556                     }
19557                 }
19558             }
19559
19560             return oDDs;
19561         },
19562
19563         /**
19564          * Returns true if the specified dd target is a legal target for
19565          * the specifice drag obj
19566          * @method isLegalTarget
19567          * @param {DragDrop} the drag obj
19568          * @param {DragDrop} the target
19569          * @return {boolean} true if the target is a legal target for the
19570          * dd obj
19571          * @static
19572          */
19573         isLegalTarget: function (oDD, oTargetDD) {
19574             var targets = this.getRelated(oDD, true);
19575             for (var i=0, len=targets.length;i<len;++i) {
19576                 if (targets[i].id == oTargetDD.id) {
19577                     return true;
19578                 }
19579             }
19580
19581             return false;
19582         },
19583
19584         /**
19585          * My goal is to be able to transparently determine if an object is
19586          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19587          * returns "object", oDD.constructor.toString() always returns
19588          * "DragDrop" and not the name of the subclass.  So for now it just
19589          * evaluates a well-known variable in DragDrop.
19590          * @method isTypeOfDD
19591          * @param {Object} the object to evaluate
19592          * @return {boolean} true if typeof oDD = DragDrop
19593          * @static
19594          */
19595         isTypeOfDD: function (oDD) {
19596             return (oDD && oDD.__ygDragDrop);
19597         },
19598
19599         /**
19600          * Utility function to determine if a given element has been
19601          * registered as a drag drop handle for the given Drag Drop object.
19602          * @method isHandle
19603          * @param {String} id the element id to check
19604          * @return {boolean} true if this element is a DragDrop handle, false
19605          * otherwise
19606          * @static
19607          */
19608         isHandle: function(sDDId, sHandleId) {
19609             return ( this.handleIds[sDDId] &&
19610                             this.handleIds[sDDId][sHandleId] );
19611         },
19612
19613         /**
19614          * Returns the DragDrop instance for a given id
19615          * @method getDDById
19616          * @param {String} id the id of the DragDrop object
19617          * @return {DragDrop} the drag drop object, null if it is not found
19618          * @static
19619          */
19620         getDDById: function(id) {
19621             for (var i in this.ids) {
19622                 if (this.ids[i][id]) {
19623                     return this.ids[i][id];
19624                 }
19625             }
19626             return null;
19627         },
19628
19629         /**
19630          * Fired after a registered DragDrop object gets the mousedown event.
19631          * Sets up the events required to track the object being dragged
19632          * @method handleMouseDown
19633          * @param {Event} e the event
19634          * @param oDD the DragDrop object being dragged
19635          * @private
19636          * @static
19637          */
19638         handleMouseDown: function(e, oDD) {
19639             if(Roo.QuickTips){
19640                 Roo.QuickTips.disable();
19641             }
19642             this.currentTarget = e.getTarget();
19643
19644             this.dragCurrent = oDD;
19645
19646             var el = oDD.getEl();
19647
19648             // track start position
19649             this.startX = e.getPageX();
19650             this.startY = e.getPageY();
19651
19652             this.deltaX = this.startX - el.offsetLeft;
19653             this.deltaY = this.startY - el.offsetTop;
19654
19655             this.dragThreshMet = false;
19656
19657             this.clickTimeout = setTimeout(
19658                     function() {
19659                         var DDM = Roo.dd.DDM;
19660                         DDM.startDrag(DDM.startX, DDM.startY);
19661                     },
19662                     this.clickTimeThresh );
19663         },
19664
19665         /**
19666          * Fired when either the drag pixel threshol or the mousedown hold
19667          * time threshold has been met.
19668          * @method startDrag
19669          * @param x {int} the X position of the original mousedown
19670          * @param y {int} the Y position of the original mousedown
19671          * @static
19672          */
19673         startDrag: function(x, y) {
19674             clearTimeout(this.clickTimeout);
19675             if (this.dragCurrent) {
19676                 this.dragCurrent.b4StartDrag(x, y);
19677                 this.dragCurrent.startDrag(x, y);
19678             }
19679             this.dragThreshMet = true;
19680         },
19681
19682         /**
19683          * Internal function to handle the mouseup event.  Will be invoked
19684          * from the context of the document.
19685          * @method handleMouseUp
19686          * @param {Event} e the event
19687          * @private
19688          * @static
19689          */
19690         handleMouseUp: function(e) {
19691
19692             if(Roo.QuickTips){
19693                 Roo.QuickTips.enable();
19694             }
19695             if (! this.dragCurrent) {
19696                 return;
19697             }
19698
19699             clearTimeout(this.clickTimeout);
19700
19701             if (this.dragThreshMet) {
19702                 this.fireEvents(e, true);
19703             } else {
19704             }
19705
19706             this.stopDrag(e);
19707
19708             this.stopEvent(e);
19709         },
19710
19711         /**
19712          * Utility to stop event propagation and event default, if these
19713          * features are turned on.
19714          * @method stopEvent
19715          * @param {Event} e the event as returned by this.getEvent()
19716          * @static
19717          */
19718         stopEvent: function(e){
19719             if(this.stopPropagation) {
19720                 e.stopPropagation();
19721             }
19722
19723             if (this.preventDefault) {
19724                 e.preventDefault();
19725             }
19726         },
19727
19728         /**
19729          * Internal function to clean up event handlers after the drag
19730          * operation is complete
19731          * @method stopDrag
19732          * @param {Event} e the event
19733          * @private
19734          * @static
19735          */
19736         stopDrag: function(e) {
19737             // Fire the drag end event for the item that was dragged
19738             if (this.dragCurrent) {
19739                 if (this.dragThreshMet) {
19740                     this.dragCurrent.b4EndDrag(e);
19741                     this.dragCurrent.endDrag(e);
19742                 }
19743
19744                 this.dragCurrent.onMouseUp(e);
19745             }
19746
19747             this.dragCurrent = null;
19748             this.dragOvers = {};
19749         },
19750
19751         /**
19752          * Internal function to handle the mousemove event.  Will be invoked
19753          * from the context of the html element.
19754          *
19755          * @TODO figure out what we can do about mouse events lost when the
19756          * user drags objects beyond the window boundary.  Currently we can
19757          * detect this in internet explorer by verifying that the mouse is
19758          * down during the mousemove event.  Firefox doesn't give us the
19759          * button state on the mousemove event.
19760          * @method handleMouseMove
19761          * @param {Event} e the event
19762          * @private
19763          * @static
19764          */
19765         handleMouseMove: function(e) {
19766             if (! this.dragCurrent) {
19767                 return true;
19768             }
19769
19770             // var button = e.which || e.button;
19771
19772             // check for IE mouseup outside of page boundary
19773             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19774                 this.stopEvent(e);
19775                 return this.handleMouseUp(e);
19776             }
19777
19778             if (!this.dragThreshMet) {
19779                 var diffX = Math.abs(this.startX - e.getPageX());
19780                 var diffY = Math.abs(this.startY - e.getPageY());
19781                 if (diffX > this.clickPixelThresh ||
19782                             diffY > this.clickPixelThresh) {
19783                     this.startDrag(this.startX, this.startY);
19784                 }
19785             }
19786
19787             if (this.dragThreshMet) {
19788                 this.dragCurrent.b4Drag(e);
19789                 this.dragCurrent.onDrag(e);
19790                 if(!this.dragCurrent.moveOnly){
19791                     this.fireEvents(e, false);
19792                 }
19793             }
19794
19795             this.stopEvent(e);
19796
19797             return true;
19798         },
19799
19800         /**
19801          * Iterates over all of the DragDrop elements to find ones we are
19802          * hovering over or dropping on
19803          * @method fireEvents
19804          * @param {Event} e the event
19805          * @param {boolean} isDrop is this a drop op or a mouseover op?
19806          * @private
19807          * @static
19808          */
19809         fireEvents: function(e, isDrop) {
19810             var dc = this.dragCurrent;
19811
19812             // If the user did the mouse up outside of the window, we could
19813             // get here even though we have ended the drag.
19814             if (!dc || dc.isLocked()) {
19815                 return;
19816             }
19817
19818             var pt = e.getPoint();
19819
19820             // cache the previous dragOver array
19821             var oldOvers = [];
19822
19823             var outEvts   = [];
19824             var overEvts  = [];
19825             var dropEvts  = [];
19826             var enterEvts = [];
19827
19828             // Check to see if the object(s) we were hovering over is no longer
19829             // being hovered over so we can fire the onDragOut event
19830             for (var i in this.dragOvers) {
19831
19832                 var ddo = this.dragOvers[i];
19833
19834                 if (! this.isTypeOfDD(ddo)) {
19835                     continue;
19836                 }
19837
19838                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19839                     outEvts.push( ddo );
19840                 }
19841
19842                 oldOvers[i] = true;
19843                 delete this.dragOvers[i];
19844             }
19845
19846             for (var sGroup in dc.groups) {
19847
19848                 if ("string" != typeof sGroup) {
19849                     continue;
19850                 }
19851
19852                 for (i in this.ids[sGroup]) {
19853                     var oDD = this.ids[sGroup][i];
19854                     if (! this.isTypeOfDD(oDD)) {
19855                         continue;
19856                     }
19857
19858                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19859                         if (this.isOverTarget(pt, oDD, this.mode)) {
19860                             // look for drop interactions
19861                             if (isDrop) {
19862                                 dropEvts.push( oDD );
19863                             // look for drag enter and drag over interactions
19864                             } else {
19865
19866                                 // initial drag over: dragEnter fires
19867                                 if (!oldOvers[oDD.id]) {
19868                                     enterEvts.push( oDD );
19869                                 // subsequent drag overs: dragOver fires
19870                                 } else {
19871                                     overEvts.push( oDD );
19872                                 }
19873
19874                                 this.dragOvers[oDD.id] = oDD;
19875                             }
19876                         }
19877                     }
19878                 }
19879             }
19880
19881             if (this.mode) {
19882                 if (outEvts.length) {
19883                     dc.b4DragOut(e, outEvts);
19884                     dc.onDragOut(e, outEvts);
19885                 }
19886
19887                 if (enterEvts.length) {
19888                     dc.onDragEnter(e, enterEvts);
19889                 }
19890
19891                 if (overEvts.length) {
19892                     dc.b4DragOver(e, overEvts);
19893                     dc.onDragOver(e, overEvts);
19894                 }
19895
19896                 if (dropEvts.length) {
19897                     dc.b4DragDrop(e, dropEvts);
19898                     dc.onDragDrop(e, dropEvts);
19899                 }
19900
19901             } else {
19902                 // fire dragout events
19903                 var len = 0;
19904                 for (i=0, len=outEvts.length; i<len; ++i) {
19905                     dc.b4DragOut(e, outEvts[i].id);
19906                     dc.onDragOut(e, outEvts[i].id);
19907                 }
19908
19909                 // fire enter events
19910                 for (i=0,len=enterEvts.length; i<len; ++i) {
19911                     // dc.b4DragEnter(e, oDD.id);
19912                     dc.onDragEnter(e, enterEvts[i].id);
19913                 }
19914
19915                 // fire over events
19916                 for (i=0,len=overEvts.length; i<len; ++i) {
19917                     dc.b4DragOver(e, overEvts[i].id);
19918                     dc.onDragOver(e, overEvts[i].id);
19919                 }
19920
19921                 // fire drop events
19922                 for (i=0, len=dropEvts.length; i<len; ++i) {
19923                     dc.b4DragDrop(e, dropEvts[i].id);
19924                     dc.onDragDrop(e, dropEvts[i].id);
19925                 }
19926
19927             }
19928
19929             // notify about a drop that did not find a target
19930             if (isDrop && !dropEvts.length) {
19931                 dc.onInvalidDrop(e);
19932             }
19933
19934         },
19935
19936         /**
19937          * Helper function for getting the best match from the list of drag
19938          * and drop objects returned by the drag and drop events when we are
19939          * in INTERSECT mode.  It returns either the first object that the
19940          * cursor is over, or the object that has the greatest overlap with
19941          * the dragged element.
19942          * @method getBestMatch
19943          * @param  {DragDrop[]} dds The array of drag and drop objects
19944          * targeted
19945          * @return {DragDrop}       The best single match
19946          * @static
19947          */
19948         getBestMatch: function(dds) {
19949             var winner = null;
19950             // Return null if the input is not what we expect
19951             //if (!dds || !dds.length || dds.length == 0) {
19952                // winner = null;
19953             // If there is only one item, it wins
19954             //} else if (dds.length == 1) {
19955
19956             var len = dds.length;
19957
19958             if (len == 1) {
19959                 winner = dds[0];
19960             } else {
19961                 // Loop through the targeted items
19962                 for (var i=0; i<len; ++i) {
19963                     var dd = dds[i];
19964                     // If the cursor is over the object, it wins.  If the
19965                     // cursor is over multiple matches, the first one we come
19966                     // to wins.
19967                     if (dd.cursorIsOver) {
19968                         winner = dd;
19969                         break;
19970                     // Otherwise the object with the most overlap wins
19971                     } else {
19972                         if (!winner ||
19973                             winner.overlap.getArea() < dd.overlap.getArea()) {
19974                             winner = dd;
19975                         }
19976                     }
19977                 }
19978             }
19979
19980             return winner;
19981         },
19982
19983         /**
19984          * Refreshes the cache of the top-left and bottom-right points of the
19985          * drag and drop objects in the specified group(s).  This is in the
19986          * format that is stored in the drag and drop instance, so typical
19987          * usage is:
19988          * <code>
19989          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
19990          * </code>
19991          * Alternatively:
19992          * <code>
19993          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
19994          * </code>
19995          * @TODO this really should be an indexed array.  Alternatively this
19996          * method could accept both.
19997          * @method refreshCache
19998          * @param {Object} groups an associative array of groups to refresh
19999          * @static
20000          */
20001         refreshCache: function(groups) {
20002             for (var sGroup in groups) {
20003                 if ("string" != typeof sGroup) {
20004                     continue;
20005                 }
20006                 for (var i in this.ids[sGroup]) {
20007                     var oDD = this.ids[sGroup][i];
20008
20009                     if (this.isTypeOfDD(oDD)) {
20010                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20011                         var loc = this.getLocation(oDD);
20012                         if (loc) {
20013                             this.locationCache[oDD.id] = loc;
20014                         } else {
20015                             delete this.locationCache[oDD.id];
20016                             // this will unregister the drag and drop object if
20017                             // the element is not in a usable state
20018                             // oDD.unreg();
20019                         }
20020                     }
20021                 }
20022             }
20023         },
20024
20025         /**
20026          * This checks to make sure an element exists and is in the DOM.  The
20027          * main purpose is to handle cases where innerHTML is used to remove
20028          * drag and drop objects from the DOM.  IE provides an 'unspecified
20029          * error' when trying to access the offsetParent of such an element
20030          * @method verifyEl
20031          * @param {HTMLElement} el the element to check
20032          * @return {boolean} true if the element looks usable
20033          * @static
20034          */
20035         verifyEl: function(el) {
20036             if (el) {
20037                 var parent;
20038                 if(Roo.isIE){
20039                     try{
20040                         parent = el.offsetParent;
20041                     }catch(e){}
20042                 }else{
20043                     parent = el.offsetParent;
20044                 }
20045                 if (parent) {
20046                     return true;
20047                 }
20048             }
20049
20050             return false;
20051         },
20052
20053         /**
20054          * Returns a Region object containing the drag and drop element's position
20055          * and size, including the padding configured for it
20056          * @method getLocation
20057          * @param {DragDrop} oDD the drag and drop object to get the
20058          *                       location for
20059          * @return {Roo.lib.Region} a Region object representing the total area
20060          *                             the element occupies, including any padding
20061          *                             the instance is configured for.
20062          * @static
20063          */
20064         getLocation: function(oDD) {
20065             if (! this.isTypeOfDD(oDD)) {
20066                 return null;
20067             }
20068
20069             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20070
20071             try {
20072                 pos= Roo.lib.Dom.getXY(el);
20073             } catch (e) { }
20074
20075             if (!pos) {
20076                 return null;
20077             }
20078
20079             x1 = pos[0];
20080             x2 = x1 + el.offsetWidth;
20081             y1 = pos[1];
20082             y2 = y1 + el.offsetHeight;
20083
20084             t = y1 - oDD.padding[0];
20085             r = x2 + oDD.padding[1];
20086             b = y2 + oDD.padding[2];
20087             l = x1 - oDD.padding[3];
20088
20089             return new Roo.lib.Region( t, r, b, l );
20090         },
20091
20092         /**
20093          * Checks the cursor location to see if it over the target
20094          * @method isOverTarget
20095          * @param {Roo.lib.Point} pt The point to evaluate
20096          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20097          * @return {boolean} true if the mouse is over the target
20098          * @private
20099          * @static
20100          */
20101         isOverTarget: function(pt, oTarget, intersect) {
20102             // use cache if available
20103             var loc = this.locationCache[oTarget.id];
20104             if (!loc || !this.useCache) {
20105                 loc = this.getLocation(oTarget);
20106                 this.locationCache[oTarget.id] = loc;
20107
20108             }
20109
20110             if (!loc) {
20111                 return false;
20112             }
20113
20114             oTarget.cursorIsOver = loc.contains( pt );
20115
20116             // DragDrop is using this as a sanity check for the initial mousedown
20117             // in this case we are done.  In POINT mode, if the drag obj has no
20118             // contraints, we are also done. Otherwise we need to evaluate the
20119             // location of the target as related to the actual location of the
20120             // dragged element.
20121             var dc = this.dragCurrent;
20122             if (!dc || !dc.getTargetCoord ||
20123                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20124                 return oTarget.cursorIsOver;
20125             }
20126
20127             oTarget.overlap = null;
20128
20129             // Get the current location of the drag element, this is the
20130             // location of the mouse event less the delta that represents
20131             // where the original mousedown happened on the element.  We
20132             // need to consider constraints and ticks as well.
20133             var pos = dc.getTargetCoord(pt.x, pt.y);
20134
20135             var el = dc.getDragEl();
20136             var curRegion = new Roo.lib.Region( pos.y,
20137                                                    pos.x + el.offsetWidth,
20138                                                    pos.y + el.offsetHeight,
20139                                                    pos.x );
20140
20141             var overlap = curRegion.intersect(loc);
20142
20143             if (overlap) {
20144                 oTarget.overlap = overlap;
20145                 return (intersect) ? true : oTarget.cursorIsOver;
20146             } else {
20147                 return false;
20148             }
20149         },
20150
20151         /**
20152          * unload event handler
20153          * @method _onUnload
20154          * @private
20155          * @static
20156          */
20157         _onUnload: function(e, me) {
20158             Roo.dd.DragDropMgr.unregAll();
20159         },
20160
20161         /**
20162          * Cleans up the drag and drop events and objects.
20163          * @method unregAll
20164          * @private
20165          * @static
20166          */
20167         unregAll: function() {
20168
20169             if (this.dragCurrent) {
20170                 this.stopDrag();
20171                 this.dragCurrent = null;
20172             }
20173
20174             this._execOnAll("unreg", []);
20175
20176             for (i in this.elementCache) {
20177                 delete this.elementCache[i];
20178             }
20179
20180             this.elementCache = {};
20181             this.ids = {};
20182         },
20183
20184         /**
20185          * A cache of DOM elements
20186          * @property elementCache
20187          * @private
20188          * @static
20189          */
20190         elementCache: {},
20191
20192         /**
20193          * Get the wrapper for the DOM element specified
20194          * @method getElWrapper
20195          * @param {String} id the id of the element to get
20196          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20197          * @private
20198          * @deprecated This wrapper isn't that useful
20199          * @static
20200          */
20201         getElWrapper: function(id) {
20202             var oWrapper = this.elementCache[id];
20203             if (!oWrapper || !oWrapper.el) {
20204                 oWrapper = this.elementCache[id] =
20205                     new this.ElementWrapper(Roo.getDom(id));
20206             }
20207             return oWrapper;
20208         },
20209
20210         /**
20211          * Returns the actual DOM element
20212          * @method getElement
20213          * @param {String} id the id of the elment to get
20214          * @return {Object} The element
20215          * @deprecated use Roo.getDom instead
20216          * @static
20217          */
20218         getElement: function(id) {
20219             return Roo.getDom(id);
20220         },
20221
20222         /**
20223          * Returns the style property for the DOM element (i.e.,
20224          * document.getElById(id).style)
20225          * @method getCss
20226          * @param {String} id the id of the elment to get
20227          * @return {Object} The style property of the element
20228          * @deprecated use Roo.getDom instead
20229          * @static
20230          */
20231         getCss: function(id) {
20232             var el = Roo.getDom(id);
20233             return (el) ? el.style : null;
20234         },
20235
20236         /**
20237          * Inner class for cached elements
20238          * @class DragDropMgr.ElementWrapper
20239          * @for DragDropMgr
20240          * @private
20241          * @deprecated
20242          */
20243         ElementWrapper: function(el) {
20244                 /**
20245                  * The element
20246                  * @property el
20247                  */
20248                 this.el = el || null;
20249                 /**
20250                  * The element id
20251                  * @property id
20252                  */
20253                 this.id = this.el && el.id;
20254                 /**
20255                  * A reference to the style property
20256                  * @property css
20257                  */
20258                 this.css = this.el && el.style;
20259             },
20260
20261         /**
20262          * Returns the X position of an html element
20263          * @method getPosX
20264          * @param el the element for which to get the position
20265          * @return {int} the X coordinate
20266          * @for DragDropMgr
20267          * @deprecated use Roo.lib.Dom.getX instead
20268          * @static
20269          */
20270         getPosX: function(el) {
20271             return Roo.lib.Dom.getX(el);
20272         },
20273
20274         /**
20275          * Returns the Y position of an html element
20276          * @method getPosY
20277          * @param el the element for which to get the position
20278          * @return {int} the Y coordinate
20279          * @deprecated use Roo.lib.Dom.getY instead
20280          * @static
20281          */
20282         getPosY: function(el) {
20283             return Roo.lib.Dom.getY(el);
20284         },
20285
20286         /**
20287          * Swap two nodes.  In IE, we use the native method, for others we
20288          * emulate the IE behavior
20289          * @method swapNode
20290          * @param n1 the first node to swap
20291          * @param n2 the other node to swap
20292          * @static
20293          */
20294         swapNode: function(n1, n2) {
20295             if (n1.swapNode) {
20296                 n1.swapNode(n2);
20297             } else {
20298                 var p = n2.parentNode;
20299                 var s = n2.nextSibling;
20300
20301                 if (s == n1) {
20302                     p.insertBefore(n1, n2);
20303                 } else if (n2 == n1.nextSibling) {
20304                     p.insertBefore(n2, n1);
20305                 } else {
20306                     n1.parentNode.replaceChild(n2, n1);
20307                     p.insertBefore(n1, s);
20308                 }
20309             }
20310         },
20311
20312         /**
20313          * Returns the current scroll position
20314          * @method getScroll
20315          * @private
20316          * @static
20317          */
20318         getScroll: function () {
20319             var t, l, dde=document.documentElement, db=document.body;
20320             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20321                 t = dde.scrollTop;
20322                 l = dde.scrollLeft;
20323             } else if (db) {
20324                 t = db.scrollTop;
20325                 l = db.scrollLeft;
20326             } else {
20327
20328             }
20329             return { top: t, left: l };
20330         },
20331
20332         /**
20333          * Returns the specified element style property
20334          * @method getStyle
20335          * @param {HTMLElement} el          the element
20336          * @param {string}      styleProp   the style property
20337          * @return {string} The value of the style property
20338          * @deprecated use Roo.lib.Dom.getStyle
20339          * @static
20340          */
20341         getStyle: function(el, styleProp) {
20342             return Roo.fly(el).getStyle(styleProp);
20343         },
20344
20345         /**
20346          * Gets the scrollTop
20347          * @method getScrollTop
20348          * @return {int} the document's scrollTop
20349          * @static
20350          */
20351         getScrollTop: function () { return this.getScroll().top; },
20352
20353         /**
20354          * Gets the scrollLeft
20355          * @method getScrollLeft
20356          * @return {int} the document's scrollTop
20357          * @static
20358          */
20359         getScrollLeft: function () { return this.getScroll().left; },
20360
20361         /**
20362          * Sets the x/y position of an element to the location of the
20363          * target element.
20364          * @method moveToEl
20365          * @param {HTMLElement} moveEl      The element to move
20366          * @param {HTMLElement} targetEl    The position reference element
20367          * @static
20368          */
20369         moveToEl: function (moveEl, targetEl) {
20370             var aCoord = Roo.lib.Dom.getXY(targetEl);
20371             Roo.lib.Dom.setXY(moveEl, aCoord);
20372         },
20373
20374         /**
20375          * Numeric array sort function
20376          * @method numericSort
20377          * @static
20378          */
20379         numericSort: function(a, b) { return (a - b); },
20380
20381         /**
20382          * Internal counter
20383          * @property _timeoutCount
20384          * @private
20385          * @static
20386          */
20387         _timeoutCount: 0,
20388
20389         /**
20390          * Trying to make the load order less important.  Without this we get
20391          * an error if this file is loaded before the Event Utility.
20392          * @method _addListeners
20393          * @private
20394          * @static
20395          */
20396         _addListeners: function() {
20397             var DDM = Roo.dd.DDM;
20398             if ( Roo.lib.Event && document ) {
20399                 DDM._onLoad();
20400             } else {
20401                 if (DDM._timeoutCount > 2000) {
20402                 } else {
20403                     setTimeout(DDM._addListeners, 10);
20404                     if (document && document.body) {
20405                         DDM._timeoutCount += 1;
20406                     }
20407                 }
20408             }
20409         },
20410
20411         /**
20412          * Recursively searches the immediate parent and all child nodes for
20413          * the handle element in order to determine wheter or not it was
20414          * clicked.
20415          * @method handleWasClicked
20416          * @param node the html element to inspect
20417          * @static
20418          */
20419         handleWasClicked: function(node, id) {
20420             if (this.isHandle(id, node.id)) {
20421                 return true;
20422             } else {
20423                 // check to see if this is a text node child of the one we want
20424                 var p = node.parentNode;
20425
20426                 while (p) {
20427                     if (this.isHandle(id, p.id)) {
20428                         return true;
20429                     } else {
20430                         p = p.parentNode;
20431                     }
20432                 }
20433             }
20434
20435             return false;
20436         }
20437
20438     };
20439
20440 }();
20441
20442 // shorter alias, save a few bytes
20443 Roo.dd.DDM = Roo.dd.DragDropMgr;
20444 Roo.dd.DDM._addListeners();
20445
20446 }/*
20447  * Based on:
20448  * Ext JS Library 1.1.1
20449  * Copyright(c) 2006-2007, Ext JS, LLC.
20450  *
20451  * Originally Released Under LGPL - original licence link has changed is not relivant.
20452  *
20453  * Fork - LGPL
20454  * <script type="text/javascript">
20455  */
20456
20457 /**
20458  * @class Roo.dd.DD
20459  * A DragDrop implementation where the linked element follows the
20460  * mouse cursor during a drag.
20461  * @extends Roo.dd.DragDrop
20462  * @constructor
20463  * @param {String} id the id of the linked element
20464  * @param {String} sGroup the group of related DragDrop items
20465  * @param {object} config an object containing configurable attributes
20466  *                Valid properties for DD:
20467  *                    scroll
20468  */
20469 Roo.dd.DD = function(id, sGroup, config) {
20470     if (id) {
20471         this.init(id, sGroup, config);
20472     }
20473 };
20474
20475 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20476
20477     /**
20478      * When set to true, the utility automatically tries to scroll the browser
20479      * window wehn a drag and drop element is dragged near the viewport boundary.
20480      * Defaults to true.
20481      * @property scroll
20482      * @type boolean
20483      */
20484     scroll: true,
20485
20486     /**
20487      * Sets the pointer offset to the distance between the linked element's top
20488      * left corner and the location the element was clicked
20489      * @method autoOffset
20490      * @param {int} iPageX the X coordinate of the click
20491      * @param {int} iPageY the Y coordinate of the click
20492      */
20493     autoOffset: function(iPageX, iPageY) {
20494         var x = iPageX - this.startPageX;
20495         var y = iPageY - this.startPageY;
20496         this.setDelta(x, y);
20497     },
20498
20499     /**
20500      * Sets the pointer offset.  You can call this directly to force the
20501      * offset to be in a particular location (e.g., pass in 0,0 to set it
20502      * to the center of the object)
20503      * @method setDelta
20504      * @param {int} iDeltaX the distance from the left
20505      * @param {int} iDeltaY the distance from the top
20506      */
20507     setDelta: function(iDeltaX, iDeltaY) {
20508         this.deltaX = iDeltaX;
20509         this.deltaY = iDeltaY;
20510     },
20511
20512     /**
20513      * Sets the drag element to the location of the mousedown or click event,
20514      * maintaining the cursor location relative to the location on the element
20515      * that was clicked.  Override this if you want to place the element in a
20516      * location other than where the cursor is.
20517      * @method setDragElPos
20518      * @param {int} iPageX the X coordinate of the mousedown or drag event
20519      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20520      */
20521     setDragElPos: function(iPageX, iPageY) {
20522         // the first time we do this, we are going to check to make sure
20523         // the element has css positioning
20524
20525         var el = this.getDragEl();
20526         this.alignElWithMouse(el, iPageX, iPageY);
20527     },
20528
20529     /**
20530      * Sets the element to the location of the mousedown or click event,
20531      * maintaining the cursor location relative to the location on the element
20532      * that was clicked.  Override this if you want to place the element in a
20533      * location other than where the cursor is.
20534      * @method alignElWithMouse
20535      * @param {HTMLElement} el the element to move
20536      * @param {int} iPageX the X coordinate of the mousedown or drag event
20537      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20538      */
20539     alignElWithMouse: function(el, iPageX, iPageY) {
20540         var oCoord = this.getTargetCoord(iPageX, iPageY);
20541         var fly = el.dom ? el : Roo.fly(el);
20542         if (!this.deltaSetXY) {
20543             var aCoord = [oCoord.x, oCoord.y];
20544             fly.setXY(aCoord);
20545             var newLeft = fly.getLeft(true);
20546             var newTop  = fly.getTop(true);
20547             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20548         } else {
20549             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20550         }
20551
20552         this.cachePosition(oCoord.x, oCoord.y);
20553         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20554         return oCoord;
20555     },
20556
20557     /**
20558      * Saves the most recent position so that we can reset the constraints and
20559      * tick marks on-demand.  We need to know this so that we can calculate the
20560      * number of pixels the element is offset from its original position.
20561      * @method cachePosition
20562      * @param iPageX the current x position (optional, this just makes it so we
20563      * don't have to look it up again)
20564      * @param iPageY the current y position (optional, this just makes it so we
20565      * don't have to look it up again)
20566      */
20567     cachePosition: function(iPageX, iPageY) {
20568         if (iPageX) {
20569             this.lastPageX = iPageX;
20570             this.lastPageY = iPageY;
20571         } else {
20572             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20573             this.lastPageX = aCoord[0];
20574             this.lastPageY = aCoord[1];
20575         }
20576     },
20577
20578     /**
20579      * Auto-scroll the window if the dragged object has been moved beyond the
20580      * visible window boundary.
20581      * @method autoScroll
20582      * @param {int} x the drag element's x position
20583      * @param {int} y the drag element's y position
20584      * @param {int} h the height of the drag element
20585      * @param {int} w the width of the drag element
20586      * @private
20587      */
20588     autoScroll: function(x, y, h, w) {
20589
20590         if (this.scroll) {
20591             // The client height
20592             var clientH = Roo.lib.Dom.getViewWidth();
20593
20594             // The client width
20595             var clientW = Roo.lib.Dom.getViewHeight();
20596
20597             // The amt scrolled down
20598             var st = this.DDM.getScrollTop();
20599
20600             // The amt scrolled right
20601             var sl = this.DDM.getScrollLeft();
20602
20603             // Location of the bottom of the element
20604             var bot = h + y;
20605
20606             // Location of the right of the element
20607             var right = w + x;
20608
20609             // The distance from the cursor to the bottom of the visible area,
20610             // adjusted so that we don't scroll if the cursor is beyond the
20611             // element drag constraints
20612             var toBot = (clientH + st - y - this.deltaY);
20613
20614             // The distance from the cursor to the right of the visible area
20615             var toRight = (clientW + sl - x - this.deltaX);
20616
20617
20618             // How close to the edge the cursor must be before we scroll
20619             // var thresh = (document.all) ? 100 : 40;
20620             var thresh = 40;
20621
20622             // How many pixels to scroll per autoscroll op.  This helps to reduce
20623             // clunky scrolling. IE is more sensitive about this ... it needs this
20624             // value to be higher.
20625             var scrAmt = (document.all) ? 80 : 30;
20626
20627             // Scroll down if we are near the bottom of the visible page and the
20628             // obj extends below the crease
20629             if ( bot > clientH && toBot < thresh ) {
20630                 window.scrollTo(sl, st + scrAmt);
20631             }
20632
20633             // Scroll up if the window is scrolled down and the top of the object
20634             // goes above the top border
20635             if ( y < st && st > 0 && y - st < thresh ) {
20636                 window.scrollTo(sl, st - scrAmt);
20637             }
20638
20639             // Scroll right if the obj is beyond the right border and the cursor is
20640             // near the border.
20641             if ( right > clientW && toRight < thresh ) {
20642                 window.scrollTo(sl + scrAmt, st);
20643             }
20644
20645             // Scroll left if the window has been scrolled to the right and the obj
20646             // extends past the left border
20647             if ( x < sl && sl > 0 && x - sl < thresh ) {
20648                 window.scrollTo(sl - scrAmt, st);
20649             }
20650         }
20651     },
20652
20653     /**
20654      * Finds the location the element should be placed if we want to move
20655      * it to where the mouse location less the click offset would place us.
20656      * @method getTargetCoord
20657      * @param {int} iPageX the X coordinate of the click
20658      * @param {int} iPageY the Y coordinate of the click
20659      * @return an object that contains the coordinates (Object.x and Object.y)
20660      * @private
20661      */
20662     getTargetCoord: function(iPageX, iPageY) {
20663
20664
20665         var x = iPageX - this.deltaX;
20666         var y = iPageY - this.deltaY;
20667
20668         if (this.constrainX) {
20669             if (x < this.minX) { x = this.minX; }
20670             if (x > this.maxX) { x = this.maxX; }
20671         }
20672
20673         if (this.constrainY) {
20674             if (y < this.minY) { y = this.minY; }
20675             if (y > this.maxY) { y = this.maxY; }
20676         }
20677
20678         x = this.getTick(x, this.xTicks);
20679         y = this.getTick(y, this.yTicks);
20680
20681
20682         return {x:x, y:y};
20683     },
20684
20685     /*
20686      * Sets up config options specific to this class. Overrides
20687      * Roo.dd.DragDrop, but all versions of this method through the
20688      * inheritance chain are called
20689      */
20690     applyConfig: function() {
20691         Roo.dd.DD.superclass.applyConfig.call(this);
20692         this.scroll = (this.config.scroll !== false);
20693     },
20694
20695     /*
20696      * Event that fires prior to the onMouseDown event.  Overrides
20697      * Roo.dd.DragDrop.
20698      */
20699     b4MouseDown: function(e) {
20700         // this.resetConstraints();
20701         this.autoOffset(e.getPageX(),
20702                             e.getPageY());
20703     },
20704
20705     /*
20706      * Event that fires prior to the onDrag event.  Overrides
20707      * Roo.dd.DragDrop.
20708      */
20709     b4Drag: function(e) {
20710         this.setDragElPos(e.getPageX(),
20711                             e.getPageY());
20712     },
20713
20714     toString: function() {
20715         return ("DD " + this.id);
20716     }
20717
20718     //////////////////////////////////////////////////////////////////////////
20719     // Debugging ygDragDrop events that can be overridden
20720     //////////////////////////////////////////////////////////////////////////
20721     /*
20722     startDrag: function(x, y) {
20723     },
20724
20725     onDrag: function(e) {
20726     },
20727
20728     onDragEnter: function(e, id) {
20729     },
20730
20731     onDragOver: function(e, id) {
20732     },
20733
20734     onDragOut: function(e, id) {
20735     },
20736
20737     onDragDrop: function(e, id) {
20738     },
20739
20740     endDrag: function(e) {
20741     }
20742
20743     */
20744
20745 });/*
20746  * Based on:
20747  * Ext JS Library 1.1.1
20748  * Copyright(c) 2006-2007, Ext JS, LLC.
20749  *
20750  * Originally Released Under LGPL - original licence link has changed is not relivant.
20751  *
20752  * Fork - LGPL
20753  * <script type="text/javascript">
20754  */
20755
20756 /**
20757  * @class Roo.dd.DDProxy
20758  * A DragDrop implementation that inserts an empty, bordered div into
20759  * the document that follows the cursor during drag operations.  At the time of
20760  * the click, the frame div is resized to the dimensions of the linked html
20761  * element, and moved to the exact location of the linked element.
20762  *
20763  * References to the "frame" element refer to the single proxy element that
20764  * was created to be dragged in place of all DDProxy elements on the
20765  * page.
20766  *
20767  * @extends Roo.dd.DD
20768  * @constructor
20769  * @param {String} id the id of the linked html element
20770  * @param {String} sGroup the group of related DragDrop objects
20771  * @param {object} config an object containing configurable attributes
20772  *                Valid properties for DDProxy in addition to those in DragDrop:
20773  *                   resizeFrame, centerFrame, dragElId
20774  */
20775 Roo.dd.DDProxy = function(id, sGroup, config) {
20776     if (id) {
20777         this.init(id, sGroup, config);
20778         this.initFrame();
20779     }
20780 };
20781
20782 /**
20783  * The default drag frame div id
20784  * @property Roo.dd.DDProxy.dragElId
20785  * @type String
20786  * @static
20787  */
20788 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20789
20790 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20791
20792     /**
20793      * By default we resize the drag frame to be the same size as the element
20794      * we want to drag (this is to get the frame effect).  We can turn it off
20795      * if we want a different behavior.
20796      * @property resizeFrame
20797      * @type boolean
20798      */
20799     resizeFrame: true,
20800
20801     /**
20802      * By default the frame is positioned exactly where the drag element is, so
20803      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20804      * you do not have constraints on the obj is to have the drag frame centered
20805      * around the cursor.  Set centerFrame to true for this effect.
20806      * @property centerFrame
20807      * @type boolean
20808      */
20809     centerFrame: false,
20810
20811     /**
20812      * Creates the proxy element if it does not yet exist
20813      * @method createFrame
20814      */
20815     createFrame: function() {
20816         var self = this;
20817         var body = document.body;
20818
20819         if (!body || !body.firstChild) {
20820             setTimeout( function() { self.createFrame(); }, 50 );
20821             return;
20822         }
20823
20824         var div = this.getDragEl();
20825
20826         if (!div) {
20827             div    = document.createElement("div");
20828             div.id = this.dragElId;
20829             var s  = div.style;
20830
20831             s.position   = "absolute";
20832             s.visibility = "hidden";
20833             s.cursor     = "move";
20834             s.border     = "2px solid #aaa";
20835             s.zIndex     = 999;
20836
20837             // appendChild can blow up IE if invoked prior to the window load event
20838             // while rendering a table.  It is possible there are other scenarios
20839             // that would cause this to happen as well.
20840             body.insertBefore(div, body.firstChild);
20841         }
20842     },
20843
20844     /**
20845      * Initialization for the drag frame element.  Must be called in the
20846      * constructor of all subclasses
20847      * @method initFrame
20848      */
20849     initFrame: function() {
20850         this.createFrame();
20851     },
20852
20853     applyConfig: function() {
20854         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20855
20856         this.resizeFrame = (this.config.resizeFrame !== false);
20857         this.centerFrame = (this.config.centerFrame);
20858         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20859     },
20860
20861     /**
20862      * Resizes the drag frame to the dimensions of the clicked object, positions
20863      * it over the object, and finally displays it
20864      * @method showFrame
20865      * @param {int} iPageX X click position
20866      * @param {int} iPageY Y click position
20867      * @private
20868      */
20869     showFrame: function(iPageX, iPageY) {
20870         var el = this.getEl();
20871         var dragEl = this.getDragEl();
20872         var s = dragEl.style;
20873
20874         this._resizeProxy();
20875
20876         if (this.centerFrame) {
20877             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20878                            Math.round(parseInt(s.height, 10)/2) );
20879         }
20880
20881         this.setDragElPos(iPageX, iPageY);
20882
20883         Roo.fly(dragEl).show();
20884     },
20885
20886     /**
20887      * The proxy is automatically resized to the dimensions of the linked
20888      * element when a drag is initiated, unless resizeFrame is set to false
20889      * @method _resizeProxy
20890      * @private
20891      */
20892     _resizeProxy: function() {
20893         if (this.resizeFrame) {
20894             var el = this.getEl();
20895             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
20896         }
20897     },
20898
20899     // overrides Roo.dd.DragDrop
20900     b4MouseDown: function(e) {
20901         var x = e.getPageX();
20902         var y = e.getPageY();
20903         this.autoOffset(x, y);
20904         this.setDragElPos(x, y);
20905     },
20906
20907     // overrides Roo.dd.DragDrop
20908     b4StartDrag: function(x, y) {
20909         // show the drag frame
20910         this.showFrame(x, y);
20911     },
20912
20913     // overrides Roo.dd.DragDrop
20914     b4EndDrag: function(e) {
20915         Roo.fly(this.getDragEl()).hide();
20916     },
20917
20918     // overrides Roo.dd.DragDrop
20919     // By default we try to move the element to the last location of the frame.
20920     // This is so that the default behavior mirrors that of Roo.dd.DD.
20921     endDrag: function(e) {
20922
20923         var lel = this.getEl();
20924         var del = this.getDragEl();
20925
20926         // Show the drag frame briefly so we can get its position
20927         del.style.visibility = "";
20928
20929         this.beforeMove();
20930         // Hide the linked element before the move to get around a Safari
20931         // rendering bug.
20932         lel.style.visibility = "hidden";
20933         Roo.dd.DDM.moveToEl(lel, del);
20934         del.style.visibility = "hidden";
20935         lel.style.visibility = "";
20936
20937         this.afterDrag();
20938     },
20939
20940     beforeMove : function(){
20941
20942     },
20943
20944     afterDrag : function(){
20945
20946     },
20947
20948     toString: function() {
20949         return ("DDProxy " + this.id);
20950     }
20951
20952 });
20953 /*
20954  * Based on:
20955  * Ext JS Library 1.1.1
20956  * Copyright(c) 2006-2007, Ext JS, LLC.
20957  *
20958  * Originally Released Under LGPL - original licence link has changed is not relivant.
20959  *
20960  * Fork - LGPL
20961  * <script type="text/javascript">
20962  */
20963
20964  /**
20965  * @class Roo.dd.DDTarget
20966  * A DragDrop implementation that does not move, but can be a drop
20967  * target.  You would get the same result by simply omitting implementation
20968  * for the event callbacks, but this way we reduce the processing cost of the
20969  * event listener and the callbacks.
20970  * @extends Roo.dd.DragDrop
20971  * @constructor
20972  * @param {String} id the id of the element that is a drop target
20973  * @param {String} sGroup the group of related DragDrop objects
20974  * @param {object} config an object containing configurable attributes
20975  *                 Valid properties for DDTarget in addition to those in
20976  *                 DragDrop:
20977  *                    none
20978  */
20979 Roo.dd.DDTarget = function(id, sGroup, config) {
20980     if (id) {
20981         this.initTarget(id, sGroup, config);
20982     }
20983     if (config.listeners || config.events) { 
20984        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
20985             listeners : config.listeners || {}, 
20986             events : config.events || {} 
20987         });    
20988     }
20989 };
20990
20991 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
20992 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
20993     toString: function() {
20994         return ("DDTarget " + this.id);
20995     }
20996 });
20997 /*
20998  * Based on:
20999  * Ext JS Library 1.1.1
21000  * Copyright(c) 2006-2007, Ext JS, LLC.
21001  *
21002  * Originally Released Under LGPL - original licence link has changed is not relivant.
21003  *
21004  * Fork - LGPL
21005  * <script type="text/javascript">
21006  */
21007  
21008
21009 /**
21010  * @class Roo.dd.ScrollManager
21011  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21012  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21013  * @singleton
21014  */
21015 Roo.dd.ScrollManager = function(){
21016     var ddm = Roo.dd.DragDropMgr;
21017     var els = {};
21018     var dragEl = null;
21019     var proc = {};
21020     
21021     
21022     
21023     var onStop = function(e){
21024         dragEl = null;
21025         clearProc();
21026     };
21027     
21028     var triggerRefresh = function(){
21029         if(ddm.dragCurrent){
21030              ddm.refreshCache(ddm.dragCurrent.groups);
21031         }
21032     };
21033     
21034     var doScroll = function(){
21035         if(ddm.dragCurrent){
21036             var dds = Roo.dd.ScrollManager;
21037             if(!dds.animate){
21038                 if(proc.el.scroll(proc.dir, dds.increment)){
21039                     triggerRefresh();
21040                 }
21041             }else{
21042                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21043             }
21044         }
21045     };
21046     
21047     var clearProc = function(){
21048         if(proc.id){
21049             clearInterval(proc.id);
21050         }
21051         proc.id = 0;
21052         proc.el = null;
21053         proc.dir = "";
21054     };
21055     
21056     var startProc = function(el, dir){
21057          Roo.log('scroll startproc');
21058         clearProc();
21059         proc.el = el;
21060         proc.dir = dir;
21061         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21062     };
21063     
21064     var onFire = function(e, isDrop){
21065        
21066         if(isDrop || !ddm.dragCurrent){ return; }
21067         var dds = Roo.dd.ScrollManager;
21068         if(!dragEl || dragEl != ddm.dragCurrent){
21069             dragEl = ddm.dragCurrent;
21070             // refresh regions on drag start
21071             dds.refreshCache();
21072         }
21073         
21074         var xy = Roo.lib.Event.getXY(e);
21075         var pt = new Roo.lib.Point(xy[0], xy[1]);
21076         for(var id in els){
21077             var el = els[id], r = el._region;
21078             if(r && r.contains(pt) && el.isScrollable()){
21079                 if(r.bottom - pt.y <= dds.thresh){
21080                     if(proc.el != el){
21081                         startProc(el, "down");
21082                     }
21083                     return;
21084                 }else if(r.right - pt.x <= dds.thresh){
21085                     if(proc.el != el){
21086                         startProc(el, "left");
21087                     }
21088                     return;
21089                 }else if(pt.y - r.top <= dds.thresh){
21090                     if(proc.el != el){
21091                         startProc(el, "up");
21092                     }
21093                     return;
21094                 }else if(pt.x - r.left <= dds.thresh){
21095                     if(proc.el != el){
21096                         startProc(el, "right");
21097                     }
21098                     return;
21099                 }
21100             }
21101         }
21102         clearProc();
21103     };
21104     
21105     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21106     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21107     
21108     return {
21109         /**
21110          * Registers new overflow element(s) to auto scroll
21111          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21112          */
21113         register : function(el){
21114             if(el instanceof Array){
21115                 for(var i = 0, len = el.length; i < len; i++) {
21116                         this.register(el[i]);
21117                 }
21118             }else{
21119                 el = Roo.get(el);
21120                 els[el.id] = el;
21121             }
21122             Roo.dd.ScrollManager.els = els;
21123         },
21124         
21125         /**
21126          * Unregisters overflow element(s) so they are no longer scrolled
21127          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21128          */
21129         unregister : function(el){
21130             if(el instanceof Array){
21131                 for(var i = 0, len = el.length; i < len; i++) {
21132                         this.unregister(el[i]);
21133                 }
21134             }else{
21135                 el = Roo.get(el);
21136                 delete els[el.id];
21137             }
21138         },
21139         
21140         /**
21141          * The number of pixels from the edge of a container the pointer needs to be to 
21142          * trigger scrolling (defaults to 25)
21143          * @type Number
21144          */
21145         thresh : 25,
21146         
21147         /**
21148          * The number of pixels to scroll in each scroll increment (defaults to 50)
21149          * @type Number
21150          */
21151         increment : 100,
21152         
21153         /**
21154          * The frequency of scrolls in milliseconds (defaults to 500)
21155          * @type Number
21156          */
21157         frequency : 500,
21158         
21159         /**
21160          * True to animate the scroll (defaults to true)
21161          * @type Boolean
21162          */
21163         animate: true,
21164         
21165         /**
21166          * The animation duration in seconds - 
21167          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21168          * @type Number
21169          */
21170         animDuration: .4,
21171         
21172         /**
21173          * Manually trigger a cache refresh.
21174          */
21175         refreshCache : function(){
21176             for(var id in els){
21177                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21178                     els[id]._region = els[id].getRegion();
21179                 }
21180             }
21181         }
21182     };
21183 }();/*
21184  * Based on:
21185  * Ext JS Library 1.1.1
21186  * Copyright(c) 2006-2007, Ext JS, LLC.
21187  *
21188  * Originally Released Under LGPL - original licence link has changed is not relivant.
21189  *
21190  * Fork - LGPL
21191  * <script type="text/javascript">
21192  */
21193  
21194
21195 /**
21196  * @class Roo.dd.Registry
21197  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21198  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21199  * @singleton
21200  */
21201 Roo.dd.Registry = function(){
21202     var elements = {}; 
21203     var handles = {}; 
21204     var autoIdSeed = 0;
21205
21206     var getId = function(el, autogen){
21207         if(typeof el == "string"){
21208             return el;
21209         }
21210         var id = el.id;
21211         if(!id && autogen !== false){
21212             id = "roodd-" + (++autoIdSeed);
21213             el.id = id;
21214         }
21215         return id;
21216     };
21217     
21218     return {
21219     /**
21220      * Register a drag drop element
21221      * @param {String|HTMLElement} element The id or DOM node to register
21222      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21223      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21224      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21225      * populated in the data object (if applicable):
21226      * <pre>
21227 Value      Description<br />
21228 ---------  ------------------------------------------<br />
21229 handles    Array of DOM nodes that trigger dragging<br />
21230            for the element being registered<br />
21231 isHandle   True if the element passed in triggers<br />
21232            dragging itself, else false
21233 </pre>
21234      */
21235         register : function(el, data){
21236             data = data || {};
21237             if(typeof el == "string"){
21238                 el = document.getElementById(el);
21239             }
21240             data.ddel = el;
21241             elements[getId(el)] = data;
21242             if(data.isHandle !== false){
21243                 handles[data.ddel.id] = data;
21244             }
21245             if(data.handles){
21246                 var hs = data.handles;
21247                 for(var i = 0, len = hs.length; i < len; i++){
21248                         handles[getId(hs[i])] = data;
21249                 }
21250             }
21251         },
21252
21253     /**
21254      * Unregister a drag drop element
21255      * @param {String|HTMLElement}  element The id or DOM node to unregister
21256      */
21257         unregister : function(el){
21258             var id = getId(el, false);
21259             var data = elements[id];
21260             if(data){
21261                 delete elements[id];
21262                 if(data.handles){
21263                     var hs = data.handles;
21264                     for(var i = 0, len = hs.length; i < len; i++){
21265                         delete handles[getId(hs[i], false)];
21266                     }
21267                 }
21268             }
21269         },
21270
21271     /**
21272      * Returns the handle registered for a DOM Node by id
21273      * @param {String|HTMLElement} id The DOM node or id to look up
21274      * @return {Object} handle The custom handle data
21275      */
21276         getHandle : function(id){
21277             if(typeof id != "string"){ // must be element?
21278                 id = id.id;
21279             }
21280             return handles[id];
21281         },
21282
21283     /**
21284      * Returns the handle that is registered for the DOM node that is the target of the event
21285      * @param {Event} e The event
21286      * @return {Object} handle The custom handle data
21287      */
21288         getHandleFromEvent : function(e){
21289             var t = Roo.lib.Event.getTarget(e);
21290             return t ? handles[t.id] : null;
21291         },
21292
21293     /**
21294      * Returns a custom data object that is registered for a DOM node by id
21295      * @param {String|HTMLElement} id The DOM node or id to look up
21296      * @return {Object} data The custom data
21297      */
21298         getTarget : function(id){
21299             if(typeof id != "string"){ // must be element?
21300                 id = id.id;
21301             }
21302             return elements[id];
21303         },
21304
21305     /**
21306      * Returns a custom data object that is registered for the DOM node that is the target of the event
21307      * @param {Event} e The event
21308      * @return {Object} data The custom data
21309      */
21310         getTargetFromEvent : function(e){
21311             var t = Roo.lib.Event.getTarget(e);
21312             return t ? elements[t.id] || handles[t.id] : null;
21313         }
21314     };
21315 }();/*
21316  * Based on:
21317  * Ext JS Library 1.1.1
21318  * Copyright(c) 2006-2007, Ext JS, LLC.
21319  *
21320  * Originally Released Under LGPL - original licence link has changed is not relivant.
21321  *
21322  * Fork - LGPL
21323  * <script type="text/javascript">
21324  */
21325  
21326
21327 /**
21328  * @class Roo.dd.StatusProxy
21329  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21330  * default drag proxy used by all Roo.dd components.
21331  * @constructor
21332  * @param {Object} config
21333  */
21334 Roo.dd.StatusProxy = function(config){
21335     Roo.apply(this, config);
21336     this.id = this.id || Roo.id();
21337     this.el = new Roo.Layer({
21338         dh: {
21339             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21340                 {tag: "div", cls: "x-dd-drop-icon"},
21341                 {tag: "div", cls: "x-dd-drag-ghost"}
21342             ]
21343         }, 
21344         shadow: !config || config.shadow !== false
21345     });
21346     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21347     this.dropStatus = this.dropNotAllowed;
21348 };
21349
21350 Roo.dd.StatusProxy.prototype = {
21351     /**
21352      * @cfg {String} dropAllowed
21353      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21354      */
21355     dropAllowed : "x-dd-drop-ok",
21356     /**
21357      * @cfg {String} dropNotAllowed
21358      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21359      */
21360     dropNotAllowed : "x-dd-drop-nodrop",
21361
21362     /**
21363      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21364      * over the current target element.
21365      * @param {String} cssClass The css class for the new drop status indicator image
21366      */
21367     setStatus : function(cssClass){
21368         cssClass = cssClass || this.dropNotAllowed;
21369         if(this.dropStatus != cssClass){
21370             this.el.replaceClass(this.dropStatus, cssClass);
21371             this.dropStatus = cssClass;
21372         }
21373     },
21374
21375     /**
21376      * Resets the status indicator to the default dropNotAllowed value
21377      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21378      */
21379     reset : function(clearGhost){
21380         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21381         this.dropStatus = this.dropNotAllowed;
21382         if(clearGhost){
21383             this.ghost.update("");
21384         }
21385     },
21386
21387     /**
21388      * Updates the contents of the ghost element
21389      * @param {String} html The html that will replace the current innerHTML of the ghost element
21390      */
21391     update : function(html){
21392         if(typeof html == "string"){
21393             this.ghost.update(html);
21394         }else{
21395             this.ghost.update("");
21396             html.style.margin = "0";
21397             this.ghost.dom.appendChild(html);
21398         }
21399         // ensure float = none set?? cant remember why though.
21400         var el = this.ghost.dom.firstChild;
21401                 if(el){
21402                         Roo.fly(el).setStyle('float', 'none');
21403                 }
21404     },
21405     
21406     /**
21407      * Returns the underlying proxy {@link Roo.Layer}
21408      * @return {Roo.Layer} el
21409     */
21410     getEl : function(){
21411         return this.el;
21412     },
21413
21414     /**
21415      * Returns the ghost element
21416      * @return {Roo.Element} el
21417      */
21418     getGhost : function(){
21419         return this.ghost;
21420     },
21421
21422     /**
21423      * Hides the proxy
21424      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21425      */
21426     hide : function(clear){
21427         this.el.hide();
21428         if(clear){
21429             this.reset(true);
21430         }
21431     },
21432
21433     /**
21434      * Stops the repair animation if it's currently running
21435      */
21436     stop : function(){
21437         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21438             this.anim.stop();
21439         }
21440     },
21441
21442     /**
21443      * Displays this proxy
21444      */
21445     show : function(){
21446         this.el.show();
21447     },
21448
21449     /**
21450      * Force the Layer to sync its shadow and shim positions to the element
21451      */
21452     sync : function(){
21453         this.el.sync();
21454     },
21455
21456     /**
21457      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21458      * invalid drop operation by the item being dragged.
21459      * @param {Array} xy The XY position of the element ([x, y])
21460      * @param {Function} callback The function to call after the repair is complete
21461      * @param {Object} scope The scope in which to execute the callback
21462      */
21463     repair : function(xy, callback, scope){
21464         this.callback = callback;
21465         this.scope = scope;
21466         if(xy && this.animRepair !== false){
21467             this.el.addClass("x-dd-drag-repair");
21468             this.el.hideUnders(true);
21469             this.anim = this.el.shift({
21470                 duration: this.repairDuration || .5,
21471                 easing: 'easeOut',
21472                 xy: xy,
21473                 stopFx: true,
21474                 callback: this.afterRepair,
21475                 scope: this
21476             });
21477         }else{
21478             this.afterRepair();
21479         }
21480     },
21481
21482     // private
21483     afterRepair : function(){
21484         this.hide(true);
21485         if(typeof this.callback == "function"){
21486             this.callback.call(this.scope || this);
21487         }
21488         this.callback = null;
21489         this.scope = null;
21490     }
21491 };/*
21492  * Based on:
21493  * Ext JS Library 1.1.1
21494  * Copyright(c) 2006-2007, Ext JS, LLC.
21495  *
21496  * Originally Released Under LGPL - original licence link has changed is not relivant.
21497  *
21498  * Fork - LGPL
21499  * <script type="text/javascript">
21500  */
21501
21502 /**
21503  * @class Roo.dd.DragSource
21504  * @extends Roo.dd.DDProxy
21505  * A simple class that provides the basic implementation needed to make any element draggable.
21506  * @constructor
21507  * @param {String/HTMLElement/Element} el The container element
21508  * @param {Object} config
21509  */
21510 Roo.dd.DragSource = function(el, config){
21511     this.el = Roo.get(el);
21512     this.dragData = {};
21513     
21514     Roo.apply(this, config);
21515     
21516     if(!this.proxy){
21517         this.proxy = new Roo.dd.StatusProxy();
21518     }
21519
21520     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21521           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21522     
21523     this.dragging = false;
21524 };
21525
21526 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21527     /**
21528      * @cfg {String} dropAllowed
21529      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21530      */
21531     dropAllowed : "x-dd-drop-ok",
21532     /**
21533      * @cfg {String} dropNotAllowed
21534      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21535      */
21536     dropNotAllowed : "x-dd-drop-nodrop",
21537
21538     /**
21539      * Returns the data object associated with this drag source
21540      * @return {Object} data An object containing arbitrary data
21541      */
21542     getDragData : function(e){
21543         return this.dragData;
21544     },
21545
21546     // private
21547     onDragEnter : function(e, id){
21548         var target = Roo.dd.DragDropMgr.getDDById(id);
21549         this.cachedTarget = target;
21550         if(this.beforeDragEnter(target, e, id) !== false){
21551             if(target.isNotifyTarget){
21552                 var status = target.notifyEnter(this, e, this.dragData);
21553                 this.proxy.setStatus(status);
21554             }else{
21555                 this.proxy.setStatus(this.dropAllowed);
21556             }
21557             
21558             if(this.afterDragEnter){
21559                 /**
21560                  * An empty function by default, but provided so that you can perform a custom action
21561                  * when the dragged item enters the drop target by providing an implementation.
21562                  * @param {Roo.dd.DragDrop} target The drop target
21563                  * @param {Event} e The event object
21564                  * @param {String} id The id of the dragged element
21565                  * @method afterDragEnter
21566                  */
21567                 this.afterDragEnter(target, e, id);
21568             }
21569         }
21570     },
21571
21572     /**
21573      * An empty function by default, but provided so that you can perform a custom action
21574      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21575      * @param {Roo.dd.DragDrop} target The drop target
21576      * @param {Event} e The event object
21577      * @param {String} id The id of the dragged element
21578      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21579      */
21580     beforeDragEnter : function(target, e, id){
21581         return true;
21582     },
21583
21584     // private
21585     alignElWithMouse: function() {
21586         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21587         this.proxy.sync();
21588     },
21589
21590     // private
21591     onDragOver : function(e, id){
21592         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21593         if(this.beforeDragOver(target, e, id) !== false){
21594             if(target.isNotifyTarget){
21595                 var status = target.notifyOver(this, e, this.dragData);
21596                 this.proxy.setStatus(status);
21597             }
21598
21599             if(this.afterDragOver){
21600                 /**
21601                  * An empty function by default, but provided so that you can perform a custom action
21602                  * while the dragged item is over the drop target by providing an implementation.
21603                  * @param {Roo.dd.DragDrop} target The drop target
21604                  * @param {Event} e The event object
21605                  * @param {String} id The id of the dragged element
21606                  * @method afterDragOver
21607                  */
21608                 this.afterDragOver(target, e, id);
21609             }
21610         }
21611     },
21612
21613     /**
21614      * An empty function by default, but provided so that you can perform a custom action
21615      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21616      * @param {Roo.dd.DragDrop} target The drop target
21617      * @param {Event} e The event object
21618      * @param {String} id The id of the dragged element
21619      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21620      */
21621     beforeDragOver : function(target, e, id){
21622         return true;
21623     },
21624
21625     // private
21626     onDragOut : function(e, id){
21627         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21628         if(this.beforeDragOut(target, e, id) !== false){
21629             if(target.isNotifyTarget){
21630                 target.notifyOut(this, e, this.dragData);
21631             }
21632             this.proxy.reset();
21633             if(this.afterDragOut){
21634                 /**
21635                  * An empty function by default, but provided so that you can perform a custom action
21636                  * after the dragged item is dragged out of the target without dropping.
21637                  * @param {Roo.dd.DragDrop} target The drop target
21638                  * @param {Event} e The event object
21639                  * @param {String} id The id of the dragged element
21640                  * @method afterDragOut
21641                  */
21642                 this.afterDragOut(target, e, id);
21643             }
21644         }
21645         this.cachedTarget = null;
21646     },
21647
21648     /**
21649      * An empty function by default, but provided so that you can perform a custom action before the dragged
21650      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21651      * @param {Roo.dd.DragDrop} target The drop target
21652      * @param {Event} e The event object
21653      * @param {String} id The id of the dragged element
21654      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21655      */
21656     beforeDragOut : function(target, e, id){
21657         return true;
21658     },
21659     
21660     // private
21661     onDragDrop : function(e, id){
21662         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21663         if(this.beforeDragDrop(target, e, id) !== false){
21664             if(target.isNotifyTarget){
21665                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21666                     this.onValidDrop(target, e, id);
21667                 }else{
21668                     this.onInvalidDrop(target, e, id);
21669                 }
21670             }else{
21671                 this.onValidDrop(target, e, id);
21672             }
21673             
21674             if(this.afterDragDrop){
21675                 /**
21676                  * An empty function by default, but provided so that you can perform a custom action
21677                  * after a valid drag drop has occurred by providing an implementation.
21678                  * @param {Roo.dd.DragDrop} target The drop target
21679                  * @param {Event} e The event object
21680                  * @param {String} id The id of the dropped element
21681                  * @method afterDragDrop
21682                  */
21683                 this.afterDragDrop(target, e, id);
21684             }
21685         }
21686         delete this.cachedTarget;
21687     },
21688
21689     /**
21690      * An empty function by default, but provided so that you can perform a custom action before the dragged
21691      * item is dropped onto the target and optionally cancel the onDragDrop.
21692      * @param {Roo.dd.DragDrop} target The drop target
21693      * @param {Event} e The event object
21694      * @param {String} id The id of the dragged element
21695      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21696      */
21697     beforeDragDrop : function(target, e, id){
21698         return true;
21699     },
21700
21701     // private
21702     onValidDrop : function(target, e, id){
21703         this.hideProxy();
21704         if(this.afterValidDrop){
21705             /**
21706              * An empty function by default, but provided so that you can perform a custom action
21707              * after a valid drop has occurred by providing an implementation.
21708              * @param {Object} target The target DD 
21709              * @param {Event} e The event object
21710              * @param {String} id The id of the dropped element
21711              * @method afterInvalidDrop
21712              */
21713             this.afterValidDrop(target, e, id);
21714         }
21715     },
21716
21717     // private
21718     getRepairXY : function(e, data){
21719         return this.el.getXY();  
21720     },
21721
21722     // private
21723     onInvalidDrop : function(target, e, id){
21724         this.beforeInvalidDrop(target, e, id);
21725         if(this.cachedTarget){
21726             if(this.cachedTarget.isNotifyTarget){
21727                 this.cachedTarget.notifyOut(this, e, this.dragData);
21728             }
21729             this.cacheTarget = null;
21730         }
21731         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21732
21733         if(this.afterInvalidDrop){
21734             /**
21735              * An empty function by default, but provided so that you can perform a custom action
21736              * after an invalid drop has occurred by providing an implementation.
21737              * @param {Event} e The event object
21738              * @param {String} id The id of the dropped element
21739              * @method afterInvalidDrop
21740              */
21741             this.afterInvalidDrop(e, id);
21742         }
21743     },
21744
21745     // private
21746     afterRepair : function(){
21747         if(Roo.enableFx){
21748             this.el.highlight(this.hlColor || "c3daf9");
21749         }
21750         this.dragging = false;
21751     },
21752
21753     /**
21754      * An empty function by default, but provided so that you can perform a custom action after an invalid
21755      * drop has occurred.
21756      * @param {Roo.dd.DragDrop} target The drop target
21757      * @param {Event} e The event object
21758      * @param {String} id The id of the dragged element
21759      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21760      */
21761     beforeInvalidDrop : function(target, e, id){
21762         return true;
21763     },
21764
21765     // private
21766     handleMouseDown : function(e){
21767         if(this.dragging) {
21768             return;
21769         }
21770         var data = this.getDragData(e);
21771         if(data && this.onBeforeDrag(data, e) !== false){
21772             this.dragData = data;
21773             this.proxy.stop();
21774             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21775         } 
21776     },
21777
21778     /**
21779      * An empty function by default, but provided so that you can perform a custom action before the initial
21780      * drag event begins and optionally cancel it.
21781      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21782      * @param {Event} e The event object
21783      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21784      */
21785     onBeforeDrag : function(data, e){
21786         return true;
21787     },
21788
21789     /**
21790      * An empty function by default, but provided so that you can perform a custom action once the initial
21791      * drag event has begun.  The drag cannot be canceled from this function.
21792      * @param {Number} x The x position of the click on the dragged object
21793      * @param {Number} y The y position of the click on the dragged object
21794      */
21795     onStartDrag : Roo.emptyFn,
21796
21797     // private - YUI override
21798     startDrag : function(x, y){
21799         this.proxy.reset();
21800         this.dragging = true;
21801         this.proxy.update("");
21802         this.onInitDrag(x, y);
21803         this.proxy.show();
21804     },
21805
21806     // private
21807     onInitDrag : function(x, y){
21808         var clone = this.el.dom.cloneNode(true);
21809         clone.id = Roo.id(); // prevent duplicate ids
21810         this.proxy.update(clone);
21811         this.onStartDrag(x, y);
21812         return true;
21813     },
21814
21815     /**
21816      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21817      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21818      */
21819     getProxy : function(){
21820         return this.proxy;  
21821     },
21822
21823     /**
21824      * Hides the drag source's {@link Roo.dd.StatusProxy}
21825      */
21826     hideProxy : function(){
21827         this.proxy.hide();  
21828         this.proxy.reset(true);
21829         this.dragging = false;
21830     },
21831
21832     // private
21833     triggerCacheRefresh : function(){
21834         Roo.dd.DDM.refreshCache(this.groups);
21835     },
21836
21837     // private - override to prevent hiding
21838     b4EndDrag: function(e) {
21839     },
21840
21841     // private - override to prevent moving
21842     endDrag : function(e){
21843         this.onEndDrag(this.dragData, e);
21844     },
21845
21846     // private
21847     onEndDrag : function(data, e){
21848     },
21849     
21850     // private - pin to cursor
21851     autoOffset : function(x, y) {
21852         this.setDelta(-12, -20);
21853     }    
21854 });/*
21855  * Based on:
21856  * Ext JS Library 1.1.1
21857  * Copyright(c) 2006-2007, Ext JS, LLC.
21858  *
21859  * Originally Released Under LGPL - original licence link has changed is not relivant.
21860  *
21861  * Fork - LGPL
21862  * <script type="text/javascript">
21863  */
21864
21865
21866 /**
21867  * @class Roo.dd.DropTarget
21868  * @extends Roo.dd.DDTarget
21869  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21870  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21871  * @constructor
21872  * @param {String/HTMLElement/Element} el The container element
21873  * @param {Object} config
21874  */
21875 Roo.dd.DropTarget = function(el, config){
21876     this.el = Roo.get(el);
21877     
21878     var listeners = false; ;
21879     if (config && config.listeners) {
21880         listeners= config.listeners;
21881         delete config.listeners;
21882     }
21883     Roo.apply(this, config);
21884     
21885     if(this.containerScroll){
21886         Roo.dd.ScrollManager.register(this.el);
21887     }
21888     this.addEvents( {
21889          /**
21890          * @scope Roo.dd.DropTarget
21891          */
21892          
21893          /**
21894          * @event enter
21895          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
21896          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
21897          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
21898          * 
21899          * IMPORTANT : it should set this.overClass and this.dropAllowed
21900          * 
21901          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21902          * @param {Event} e The event
21903          * @param {Object} data An object containing arbitrary data supplied by the drag source
21904          */
21905         "enter" : true,
21906         
21907          /**
21908          * @event over
21909          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
21910          * This method will be called on every mouse movement while the drag source is over the drop target.
21911          * This default implementation simply returns the dropAllowed config value.
21912          * 
21913          * IMPORTANT : it should set this.dropAllowed
21914          * 
21915          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21916          * @param {Event} e The event
21917          * @param {Object} data An object containing arbitrary data supplied by the drag source
21918          
21919          */
21920         "over" : true,
21921         /**
21922          * @event out
21923          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
21924          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
21925          * overClass (if any) from the drop element.
21926          * 
21927          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21928          * @param {Event} e The event
21929          * @param {Object} data An object containing arbitrary data supplied by the drag source
21930          */
21931          "out" : true,
21932          
21933         /**
21934          * @event drop
21935          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
21936          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
21937          * implementation that does something to process the drop event and returns true so that the drag source's
21938          * repair action does not run.
21939          * 
21940          * IMPORTANT : it should set this.success
21941          * 
21942          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21943          * @param {Event} e The event
21944          * @param {Object} data An object containing arbitrary data supplied by the drag source
21945         */
21946          "drop" : true
21947     });
21948             
21949      
21950     Roo.dd.DropTarget.superclass.constructor.call(  this, 
21951         this.el.dom, 
21952         this.ddGroup || this.group,
21953         {
21954             isTarget: true,
21955             listeners : listeners || {} 
21956            
21957         
21958         }
21959     );
21960
21961 };
21962
21963 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
21964     /**
21965      * @cfg {String} overClass
21966      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
21967      */
21968      /**
21969      * @cfg {String} ddGroup
21970      * The drag drop group to handle drop events for
21971      */
21972      
21973     /**
21974      * @cfg {String} dropAllowed
21975      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21976      */
21977     dropAllowed : "x-dd-drop-ok",
21978     /**
21979      * @cfg {String} dropNotAllowed
21980      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21981      */
21982     dropNotAllowed : "x-dd-drop-nodrop",
21983     /**
21984      * @cfg {boolean} success
21985      * set this after drop listener.. 
21986      */
21987     success : false,
21988     /**
21989      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
21990      * if the drop point is valid for over/enter..
21991      */
21992     valid : false,
21993     // private
21994     isTarget : true,
21995
21996     // private
21997     isNotifyTarget : true,
21998     
21999     /**
22000      * @hide
22001      */
22002     notifyEnter : function(dd, e, data)
22003     {
22004         this.valid = true;
22005         this.fireEvent('enter', dd, e, data);
22006         if(this.overClass){
22007             this.el.addClass(this.overClass);
22008         }
22009         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22010             this.valid ? this.dropAllowed : this.dropNotAllowed
22011         );
22012     },
22013
22014     /**
22015      * @hide
22016      */
22017     notifyOver : function(dd, e, data)
22018     {
22019         this.valid = true;
22020         this.fireEvent('over', dd, e, data);
22021         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22022             this.valid ? this.dropAllowed : this.dropNotAllowed
22023         );
22024     },
22025
22026     /**
22027      * @hide
22028      */
22029     notifyOut : function(dd, e, data)
22030     {
22031         this.fireEvent('out', dd, e, data);
22032         if(this.overClass){
22033             this.el.removeClass(this.overClass);
22034         }
22035     },
22036
22037     /**
22038      * @hide
22039      */
22040     notifyDrop : function(dd, e, data)
22041     {
22042         this.success = false;
22043         this.fireEvent('drop', dd, e, data);
22044         return this.success;
22045     }
22046 });/*
22047  * Based on:
22048  * Ext JS Library 1.1.1
22049  * Copyright(c) 2006-2007, Ext JS, LLC.
22050  *
22051  * Originally Released Under LGPL - original licence link has changed is not relivant.
22052  *
22053  * Fork - LGPL
22054  * <script type="text/javascript">
22055  */
22056
22057
22058 /**
22059  * @class Roo.dd.DragZone
22060  * @extends Roo.dd.DragSource
22061  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22062  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22063  * @constructor
22064  * @param {String/HTMLElement/Element} el The container element
22065  * @param {Object} config
22066  */
22067 Roo.dd.DragZone = function(el, config){
22068     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22069     if(this.containerScroll){
22070         Roo.dd.ScrollManager.register(this.el);
22071     }
22072 };
22073
22074 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22075     /**
22076      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22077      * for auto scrolling during drag operations.
22078      */
22079     /**
22080      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22081      * method after a failed drop (defaults to "c3daf9" - light blue)
22082      */
22083
22084     /**
22085      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22086      * for a valid target to drag based on the mouse down. Override this method
22087      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22088      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22089      * @param {EventObject} e The mouse down event
22090      * @return {Object} The dragData
22091      */
22092     getDragData : function(e){
22093         return Roo.dd.Registry.getHandleFromEvent(e);
22094     },
22095     
22096     /**
22097      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22098      * this.dragData.ddel
22099      * @param {Number} x The x position of the click on the dragged object
22100      * @param {Number} y The y position of the click on the dragged object
22101      * @return {Boolean} true to continue the drag, false to cancel
22102      */
22103     onInitDrag : function(x, y){
22104         this.proxy.update(this.dragData.ddel.cloneNode(true));
22105         this.onStartDrag(x, y);
22106         return true;
22107     },
22108     
22109     /**
22110      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22111      */
22112     afterRepair : function(){
22113         if(Roo.enableFx){
22114             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22115         }
22116         this.dragging = false;
22117     },
22118
22119     /**
22120      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22121      * the XY of this.dragData.ddel
22122      * @param {EventObject} e The mouse up event
22123      * @return {Array} The xy location (e.g. [100, 200])
22124      */
22125     getRepairXY : function(e){
22126         return Roo.Element.fly(this.dragData.ddel).getXY();  
22127     }
22128 });/*
22129  * Based on:
22130  * Ext JS Library 1.1.1
22131  * Copyright(c) 2006-2007, Ext JS, LLC.
22132  *
22133  * Originally Released Under LGPL - original licence link has changed is not relivant.
22134  *
22135  * Fork - LGPL
22136  * <script type="text/javascript">
22137  */
22138 /**
22139  * @class Roo.dd.DropZone
22140  * @extends Roo.dd.DropTarget
22141  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22142  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22143  * @constructor
22144  * @param {String/HTMLElement/Element} el The container element
22145  * @param {Object} config
22146  */
22147 Roo.dd.DropZone = function(el, config){
22148     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22149 };
22150
22151 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22152     /**
22153      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22154      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22155      * provide your own custom lookup.
22156      * @param {Event} e The event
22157      * @return {Object} data The custom data
22158      */
22159     getTargetFromEvent : function(e){
22160         return Roo.dd.Registry.getTargetFromEvent(e);
22161     },
22162
22163     /**
22164      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22165      * that it has registered.  This method has no default implementation and should be overridden to provide
22166      * node-specific processing if necessary.
22167      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22168      * {@link #getTargetFromEvent} for this node)
22169      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22170      * @param {Event} e The event
22171      * @param {Object} data An object containing arbitrary data supplied by the drag source
22172      */
22173     onNodeEnter : function(n, dd, e, data){
22174         
22175     },
22176
22177     /**
22178      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22179      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22180      * overridden to provide the proper feedback.
22181      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22182      * {@link #getTargetFromEvent} for this node)
22183      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22184      * @param {Event} e The event
22185      * @param {Object} data An object containing arbitrary data supplied by the drag source
22186      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22187      * underlying {@link Roo.dd.StatusProxy} can be updated
22188      */
22189     onNodeOver : function(n, dd, e, data){
22190         return this.dropAllowed;
22191     },
22192
22193     /**
22194      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22195      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22196      * node-specific processing if necessary.
22197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22198      * {@link #getTargetFromEvent} for this node)
22199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22200      * @param {Event} e The event
22201      * @param {Object} data An object containing arbitrary data supplied by the drag source
22202      */
22203     onNodeOut : function(n, dd, e, data){
22204         
22205     },
22206
22207     /**
22208      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22209      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22210      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22211      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22212      * {@link #getTargetFromEvent} for this node)
22213      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22214      * @param {Event} e The event
22215      * @param {Object} data An object containing arbitrary data supplied by the drag source
22216      * @return {Boolean} True if the drop was valid, else false
22217      */
22218     onNodeDrop : function(n, dd, e, data){
22219         return false;
22220     },
22221
22222     /**
22223      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22224      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22225      * it should be overridden to provide the proper feedback if necessary.
22226      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22227      * @param {Event} e The event
22228      * @param {Object} data An object containing arbitrary data supplied by the drag source
22229      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22230      * underlying {@link Roo.dd.StatusProxy} can be updated
22231      */
22232     onContainerOver : function(dd, e, data){
22233         return this.dropNotAllowed;
22234     },
22235
22236     /**
22237      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22238      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22239      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22240      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22241      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22242      * @param {Event} e The event
22243      * @param {Object} data An object containing arbitrary data supplied by the drag source
22244      * @return {Boolean} True if the drop was valid, else false
22245      */
22246     onContainerDrop : function(dd, e, data){
22247         return false;
22248     },
22249
22250     /**
22251      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22252      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22253      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22254      * you should override this method and provide a custom implementation.
22255      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22256      * @param {Event} e The event
22257      * @param {Object} data An object containing arbitrary data supplied by the drag source
22258      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22259      * underlying {@link Roo.dd.StatusProxy} can be updated
22260      */
22261     notifyEnter : function(dd, e, data){
22262         return this.dropNotAllowed;
22263     },
22264
22265     /**
22266      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22267      * This method will be called on every mouse movement while the drag source is over the drop zone.
22268      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22269      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22270      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22271      * registered node, it will call {@link #onContainerOver}.
22272      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22273      * @param {Event} e The event
22274      * @param {Object} data An object containing arbitrary data supplied by the drag source
22275      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22276      * underlying {@link Roo.dd.StatusProxy} can be updated
22277      */
22278     notifyOver : function(dd, e, data){
22279         var n = this.getTargetFromEvent(e);
22280         if(!n){ // not over valid drop target
22281             if(this.lastOverNode){
22282                 this.onNodeOut(this.lastOverNode, dd, e, data);
22283                 this.lastOverNode = null;
22284             }
22285             return this.onContainerOver(dd, e, data);
22286         }
22287         if(this.lastOverNode != n){
22288             if(this.lastOverNode){
22289                 this.onNodeOut(this.lastOverNode, dd, e, data);
22290             }
22291             this.onNodeEnter(n, dd, e, data);
22292             this.lastOverNode = n;
22293         }
22294         return this.onNodeOver(n, dd, e, data);
22295     },
22296
22297     /**
22298      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22299      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22300      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22301      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22302      * @param {Event} e The event
22303      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22304      */
22305     notifyOut : function(dd, e, data){
22306         if(this.lastOverNode){
22307             this.onNodeOut(this.lastOverNode, dd, e, data);
22308             this.lastOverNode = null;
22309         }
22310     },
22311
22312     /**
22313      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22314      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22315      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22316      * otherwise it will call {@link #onContainerDrop}.
22317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22318      * @param {Event} e The event
22319      * @param {Object} data An object containing arbitrary data supplied by the drag source
22320      * @return {Boolean} True if the drop was valid, else false
22321      */
22322     notifyDrop : function(dd, e, data){
22323         if(this.lastOverNode){
22324             this.onNodeOut(this.lastOverNode, dd, e, data);
22325             this.lastOverNode = null;
22326         }
22327         var n = this.getTargetFromEvent(e);
22328         return n ?
22329             this.onNodeDrop(n, dd, e, data) :
22330             this.onContainerDrop(dd, e, data);
22331     },
22332
22333     // private
22334     triggerCacheRefresh : function(){
22335         Roo.dd.DDM.refreshCache(this.groups);
22336     }  
22337 });/*
22338  * Based on:
22339  * Ext JS Library 1.1.1
22340  * Copyright(c) 2006-2007, Ext JS, LLC.
22341  *
22342  * Originally Released Under LGPL - original licence link has changed is not relivant.
22343  *
22344  * Fork - LGPL
22345  * <script type="text/javascript">
22346  */
22347
22348
22349 /**
22350  * @class Roo.data.SortTypes
22351  * @singleton
22352  * Defines the default sorting (casting?) comparison functions used when sorting data.
22353  */
22354 Roo.data.SortTypes = {
22355     /**
22356      * Default sort that does nothing
22357      * @param {Mixed} s The value being converted
22358      * @return {Mixed} The comparison value
22359      */
22360     none : function(s){
22361         return s;
22362     },
22363     
22364     /**
22365      * The regular expression used to strip tags
22366      * @type {RegExp}
22367      * @property
22368      */
22369     stripTagsRE : /<\/?[^>]+>/gi,
22370     
22371     /**
22372      * Strips all HTML tags to sort on text only
22373      * @param {Mixed} s The value being converted
22374      * @return {String} The comparison value
22375      */
22376     asText : function(s){
22377         return String(s).replace(this.stripTagsRE, "");
22378     },
22379     
22380     /**
22381      * Strips all HTML tags to sort on text only - Case insensitive
22382      * @param {Mixed} s The value being converted
22383      * @return {String} The comparison value
22384      */
22385     asUCText : function(s){
22386         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22387     },
22388     
22389     /**
22390      * Case insensitive string
22391      * @param {Mixed} s The value being converted
22392      * @return {String} The comparison value
22393      */
22394     asUCString : function(s) {
22395         return String(s).toUpperCase();
22396     },
22397     
22398     /**
22399      * Date sorting
22400      * @param {Mixed} s The value being converted
22401      * @return {Number} The comparison value
22402      */
22403     asDate : function(s) {
22404         if(!s){
22405             return 0;
22406         }
22407         if(s instanceof Date){
22408             return s.getTime();
22409         }
22410         return Date.parse(String(s));
22411     },
22412     
22413     /**
22414      * Float sorting
22415      * @param {Mixed} s The value being converted
22416      * @return {Float} The comparison value
22417      */
22418     asFloat : function(s) {
22419         var val = parseFloat(String(s).replace(/,/g, ""));
22420         if(isNaN(val)) {
22421             val = 0;
22422         }
22423         return val;
22424     },
22425     
22426     /**
22427      * Integer sorting
22428      * @param {Mixed} s The value being converted
22429      * @return {Number} The comparison value
22430      */
22431     asInt : function(s) {
22432         var val = parseInt(String(s).replace(/,/g, ""));
22433         if(isNaN(val)) {
22434             val = 0;
22435         }
22436         return val;
22437     }
22438 };/*
22439  * Based on:
22440  * Ext JS Library 1.1.1
22441  * Copyright(c) 2006-2007, Ext JS, LLC.
22442  *
22443  * Originally Released Under LGPL - original licence link has changed is not relivant.
22444  *
22445  * Fork - LGPL
22446  * <script type="text/javascript">
22447  */
22448
22449 /**
22450 * @class Roo.data.Record
22451  * Instances of this class encapsulate both record <em>definition</em> information, and record
22452  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22453  * to access Records cached in an {@link Roo.data.Store} object.<br>
22454  * <p>
22455  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22456  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22457  * objects.<br>
22458  * <p>
22459  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22460  * @constructor
22461  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22462  * {@link #create}. The parameters are the same.
22463  * @param {Array} data An associative Array of data values keyed by the field name.
22464  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22465  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22466  * not specified an integer id is generated.
22467  */
22468 Roo.data.Record = function(data, id){
22469     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22470     this.data = data;
22471 };
22472
22473 /**
22474  * Generate a constructor for a specific record layout.
22475  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22476  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22477  * Each field definition object may contain the following properties: <ul>
22478  * <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,
22479  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22480  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22481  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22482  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22483  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22484  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22485  * this may be omitted.</p></li>
22486  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22487  * <ul><li>auto (Default, implies no conversion)</li>
22488  * <li>string</li>
22489  * <li>int</li>
22490  * <li>float</li>
22491  * <li>boolean</li>
22492  * <li>date</li></ul></p></li>
22493  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22494  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22495  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22496  * by the Reader into an object that will be stored in the Record. It is passed the
22497  * following parameters:<ul>
22498  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22499  * </ul></p></li>
22500  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22501  * </ul>
22502  * <br>usage:<br><pre><code>
22503 var TopicRecord = Roo.data.Record.create(
22504     {name: 'title', mapping: 'topic_title'},
22505     {name: 'author', mapping: 'username'},
22506     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22507     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22508     {name: 'lastPoster', mapping: 'user2'},
22509     {name: 'excerpt', mapping: 'post_text'}
22510 );
22511
22512 var myNewRecord = new TopicRecord({
22513     title: 'Do my job please',
22514     author: 'noobie',
22515     totalPosts: 1,
22516     lastPost: new Date(),
22517     lastPoster: 'Animal',
22518     excerpt: 'No way dude!'
22519 });
22520 myStore.add(myNewRecord);
22521 </code></pre>
22522  * @method create
22523  * @static
22524  */
22525 Roo.data.Record.create = function(o){
22526     var f = function(){
22527         f.superclass.constructor.apply(this, arguments);
22528     };
22529     Roo.extend(f, Roo.data.Record);
22530     var p = f.prototype;
22531     p.fields = new Roo.util.MixedCollection(false, function(field){
22532         return field.name;
22533     });
22534     for(var i = 0, len = o.length; i < len; i++){
22535         p.fields.add(new Roo.data.Field(o[i]));
22536     }
22537     f.getField = function(name){
22538         return p.fields.get(name);  
22539     };
22540     return f;
22541 };
22542
22543 Roo.data.Record.AUTO_ID = 1000;
22544 Roo.data.Record.EDIT = 'edit';
22545 Roo.data.Record.REJECT = 'reject';
22546 Roo.data.Record.COMMIT = 'commit';
22547
22548 Roo.data.Record.prototype = {
22549     /**
22550      * Readonly flag - true if this record has been modified.
22551      * @type Boolean
22552      */
22553     dirty : false,
22554     editing : false,
22555     error: null,
22556     modified: null,
22557
22558     // private
22559     join : function(store){
22560         this.store = store;
22561     },
22562
22563     /**
22564      * Set the named field to the specified value.
22565      * @param {String} name The name of the field to set.
22566      * @param {Object} value The value to set the field to.
22567      */
22568     set : function(name, value){
22569         if(this.data[name] == value){
22570             return;
22571         }
22572         this.dirty = true;
22573         if(!this.modified){
22574             this.modified = {};
22575         }
22576         if(typeof this.modified[name] == 'undefined'){
22577             this.modified[name] = this.data[name];
22578         }
22579         this.data[name] = value;
22580         if(!this.editing && this.store){
22581             this.store.afterEdit(this);
22582         }       
22583     },
22584
22585     /**
22586      * Get the value of the named field.
22587      * @param {String} name The name of the field to get the value of.
22588      * @return {Object} The value of the field.
22589      */
22590     get : function(name){
22591         return this.data[name]; 
22592     },
22593
22594     // private
22595     beginEdit : function(){
22596         this.editing = true;
22597         this.modified = {}; 
22598     },
22599
22600     // private
22601     cancelEdit : function(){
22602         this.editing = false;
22603         delete this.modified;
22604     },
22605
22606     // private
22607     endEdit : function(){
22608         this.editing = false;
22609         if(this.dirty && this.store){
22610             this.store.afterEdit(this);
22611         }
22612     },
22613
22614     /**
22615      * Usually called by the {@link Roo.data.Store} which owns the Record.
22616      * Rejects all changes made to the Record since either creation, or the last commit operation.
22617      * Modified fields are reverted to their original values.
22618      * <p>
22619      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22620      * of reject operations.
22621      */
22622     reject : function(){
22623         var m = this.modified;
22624         for(var n in m){
22625             if(typeof m[n] != "function"){
22626                 this.data[n] = m[n];
22627             }
22628         }
22629         this.dirty = false;
22630         delete this.modified;
22631         this.editing = false;
22632         if(this.store){
22633             this.store.afterReject(this);
22634         }
22635     },
22636
22637     /**
22638      * Usually called by the {@link Roo.data.Store} which owns the Record.
22639      * Commits all changes made to the Record since either creation, or the last commit operation.
22640      * <p>
22641      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22642      * of commit operations.
22643      */
22644     commit : function(){
22645         this.dirty = false;
22646         delete this.modified;
22647         this.editing = false;
22648         if(this.store){
22649             this.store.afterCommit(this);
22650         }
22651     },
22652
22653     // private
22654     hasError : function(){
22655         return this.error != null;
22656     },
22657
22658     // private
22659     clearError : function(){
22660         this.error = null;
22661     },
22662
22663     /**
22664      * Creates a copy of this record.
22665      * @param {String} id (optional) A new record id if you don't want to use this record's id
22666      * @return {Record}
22667      */
22668     copy : function(newId) {
22669         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22670     }
22671 };/*
22672  * Based on:
22673  * Ext JS Library 1.1.1
22674  * Copyright(c) 2006-2007, Ext JS, LLC.
22675  *
22676  * Originally Released Under LGPL - original licence link has changed is not relivant.
22677  *
22678  * Fork - LGPL
22679  * <script type="text/javascript">
22680  */
22681
22682
22683
22684 /**
22685  * @class Roo.data.Store
22686  * @extends Roo.util.Observable
22687  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22688  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22689  * <p>
22690  * 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
22691  * has no knowledge of the format of the data returned by the Proxy.<br>
22692  * <p>
22693  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22694  * instances from the data object. These records are cached and made available through accessor functions.
22695  * @constructor
22696  * Creates a new Store.
22697  * @param {Object} config A config object containing the objects needed for the Store to access data,
22698  * and read the data into Records.
22699  */
22700 Roo.data.Store = function(config){
22701     this.data = new Roo.util.MixedCollection(false);
22702     this.data.getKey = function(o){
22703         return o.id;
22704     };
22705     this.baseParams = {};
22706     // private
22707     this.paramNames = {
22708         "start" : "start",
22709         "limit" : "limit",
22710         "sort" : "sort",
22711         "dir" : "dir",
22712         "multisort" : "_multisort"
22713     };
22714
22715     if(config && config.data){
22716         this.inlineData = config.data;
22717         delete config.data;
22718     }
22719
22720     Roo.apply(this, config);
22721     
22722     if(this.reader){ // reader passed
22723         this.reader = Roo.factory(this.reader, Roo.data);
22724         this.reader.xmodule = this.xmodule || false;
22725         if(!this.recordType){
22726             this.recordType = this.reader.recordType;
22727         }
22728         if(this.reader.onMetaChange){
22729             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22730         }
22731     }
22732
22733     if(this.recordType){
22734         this.fields = this.recordType.prototype.fields;
22735     }
22736     this.modified = [];
22737
22738     this.addEvents({
22739         /**
22740          * @event datachanged
22741          * Fires when the data cache has changed, and a widget which is using this Store
22742          * as a Record cache should refresh its view.
22743          * @param {Store} this
22744          */
22745         datachanged : true,
22746         /**
22747          * @event metachange
22748          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22749          * @param {Store} this
22750          * @param {Object} meta The JSON metadata
22751          */
22752         metachange : true,
22753         /**
22754          * @event add
22755          * Fires when Records have been added to the Store
22756          * @param {Store} this
22757          * @param {Roo.data.Record[]} records The array of Records added
22758          * @param {Number} index The index at which the record(s) were added
22759          */
22760         add : true,
22761         /**
22762          * @event remove
22763          * Fires when a Record has been removed from the Store
22764          * @param {Store} this
22765          * @param {Roo.data.Record} record The Record that was removed
22766          * @param {Number} index The index at which the record was removed
22767          */
22768         remove : true,
22769         /**
22770          * @event update
22771          * Fires when a Record has been updated
22772          * @param {Store} this
22773          * @param {Roo.data.Record} record The Record that was updated
22774          * @param {String} operation The update operation being performed.  Value may be one of:
22775          * <pre><code>
22776  Roo.data.Record.EDIT
22777  Roo.data.Record.REJECT
22778  Roo.data.Record.COMMIT
22779          * </code></pre>
22780          */
22781         update : true,
22782         /**
22783          * @event clear
22784          * Fires when the data cache has been cleared.
22785          * @param {Store} this
22786          */
22787         clear : true,
22788         /**
22789          * @event beforeload
22790          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22791          * the load action will be canceled.
22792          * @param {Store} this
22793          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22794          */
22795         beforeload : true,
22796         /**
22797          * @event beforeloadadd
22798          * Fires after a new set of Records has been loaded.
22799          * @param {Store} this
22800          * @param {Roo.data.Record[]} records The Records that were loaded
22801          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22802          */
22803         beforeloadadd : true,
22804         /**
22805          * @event load
22806          * Fires after a new set of Records has been loaded, before they are added to the store.
22807          * @param {Store} this
22808          * @param {Roo.data.Record[]} records The Records that were loaded
22809          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22810          * @params {Object} return from reader
22811          */
22812         load : true,
22813         /**
22814          * @event loadexception
22815          * Fires if an exception occurs in the Proxy during loading.
22816          * Called with the signature of the Proxy's "loadexception" event.
22817          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22818          * 
22819          * @param {Proxy} 
22820          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22821          * @param {Object} load options 
22822          * @param {Object} jsonData from your request (normally this contains the Exception)
22823          */
22824         loadexception : true
22825     });
22826     
22827     if(this.proxy){
22828         this.proxy = Roo.factory(this.proxy, Roo.data);
22829         this.proxy.xmodule = this.xmodule || false;
22830         this.relayEvents(this.proxy,  ["loadexception"]);
22831     }
22832     this.sortToggle = {};
22833     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22834
22835     Roo.data.Store.superclass.constructor.call(this);
22836
22837     if(this.inlineData){
22838         this.loadData(this.inlineData);
22839         delete this.inlineData;
22840     }
22841 };
22842
22843 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22844      /**
22845     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22846     * without a remote query - used by combo/forms at present.
22847     */
22848     
22849     /**
22850     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22851     */
22852     /**
22853     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22854     */
22855     /**
22856     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22857     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22858     */
22859     /**
22860     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22861     * on any HTTP request
22862     */
22863     /**
22864     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22865     */
22866     /**
22867     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22868     */
22869     multiSort: false,
22870     /**
22871     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22872     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22873     */
22874     remoteSort : false,
22875
22876     /**
22877     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22878      * loaded or when a record is removed. (defaults to false).
22879     */
22880     pruneModifiedRecords : false,
22881
22882     // private
22883     lastOptions : null,
22884
22885     /**
22886      * Add Records to the Store and fires the add event.
22887      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22888      */
22889     add : function(records){
22890         records = [].concat(records);
22891         for(var i = 0, len = records.length; i < len; i++){
22892             records[i].join(this);
22893         }
22894         var index = this.data.length;
22895         this.data.addAll(records);
22896         this.fireEvent("add", this, records, index);
22897     },
22898
22899     /**
22900      * Remove a Record from the Store and fires the remove event.
22901      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
22902      */
22903     remove : function(record){
22904         var index = this.data.indexOf(record);
22905         this.data.removeAt(index);
22906         if(this.pruneModifiedRecords){
22907             this.modified.remove(record);
22908         }
22909         this.fireEvent("remove", this, record, index);
22910     },
22911
22912     /**
22913      * Remove all Records from the Store and fires the clear event.
22914      */
22915     removeAll : function(){
22916         this.data.clear();
22917         if(this.pruneModifiedRecords){
22918             this.modified = [];
22919         }
22920         this.fireEvent("clear", this);
22921     },
22922
22923     /**
22924      * Inserts Records to the Store at the given index and fires the add event.
22925      * @param {Number} index The start index at which to insert the passed Records.
22926      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22927      */
22928     insert : function(index, records){
22929         records = [].concat(records);
22930         for(var i = 0, len = records.length; i < len; i++){
22931             this.data.insert(index, records[i]);
22932             records[i].join(this);
22933         }
22934         this.fireEvent("add", this, records, index);
22935     },
22936
22937     /**
22938      * Get the index within the cache of the passed Record.
22939      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
22940      * @return {Number} The index of the passed Record. Returns -1 if not found.
22941      */
22942     indexOf : function(record){
22943         return this.data.indexOf(record);
22944     },
22945
22946     /**
22947      * Get the index within the cache of the Record with the passed id.
22948      * @param {String} id The id of the Record to find.
22949      * @return {Number} The index of the Record. Returns -1 if not found.
22950      */
22951     indexOfId : function(id){
22952         return this.data.indexOfKey(id);
22953     },
22954
22955     /**
22956      * Get the Record with the specified id.
22957      * @param {String} id The id of the Record to find.
22958      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
22959      */
22960     getById : function(id){
22961         return this.data.key(id);
22962     },
22963
22964     /**
22965      * Get the Record at the specified index.
22966      * @param {Number} index The index of the Record to find.
22967      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
22968      */
22969     getAt : function(index){
22970         return this.data.itemAt(index);
22971     },
22972
22973     /**
22974      * Returns a range of Records between specified indices.
22975      * @param {Number} startIndex (optional) The starting index (defaults to 0)
22976      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
22977      * @return {Roo.data.Record[]} An array of Records
22978      */
22979     getRange : function(start, end){
22980         return this.data.getRange(start, end);
22981     },
22982
22983     // private
22984     storeOptions : function(o){
22985         o = Roo.apply({}, o);
22986         delete o.callback;
22987         delete o.scope;
22988         this.lastOptions = o;
22989     },
22990
22991     /**
22992      * Loads the Record cache from the configured Proxy using the configured Reader.
22993      * <p>
22994      * If using remote paging, then the first load call must specify the <em>start</em>
22995      * and <em>limit</em> properties in the options.params property to establish the initial
22996      * position within the dataset, and the number of Records to cache on each read from the Proxy.
22997      * <p>
22998      * <strong>It is important to note that for remote data sources, loading is asynchronous,
22999      * and this call will return before the new data has been loaded. Perform any post-processing
23000      * in a callback function, or in a "load" event handler.</strong>
23001      * <p>
23002      * @param {Object} options An object containing properties which control loading options:<ul>
23003      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23004      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23005      * passed the following arguments:<ul>
23006      * <li>r : Roo.data.Record[]</li>
23007      * <li>options: Options object from the load call</li>
23008      * <li>success: Boolean success indicator</li></ul></li>
23009      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23010      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23011      * </ul>
23012      */
23013     load : function(options){
23014         options = options || {};
23015         if(this.fireEvent("beforeload", this, options) !== false){
23016             this.storeOptions(options);
23017             var p = Roo.apply(options.params || {}, this.baseParams);
23018             // if meta was not loaded from remote source.. try requesting it.
23019             if (!this.reader.metaFromRemote) {
23020                 p._requestMeta = 1;
23021             }
23022             if(this.sortInfo && this.remoteSort){
23023                 var pn = this.paramNames;
23024                 p[pn["sort"]] = this.sortInfo.field;
23025                 p[pn["dir"]] = this.sortInfo.direction;
23026             }
23027             if (this.multiSort) {
23028                 var pn = this.paramNames;
23029                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23030             }
23031             
23032             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23033         }
23034     },
23035
23036     /**
23037      * Reloads the Record cache from the configured Proxy using the configured Reader and
23038      * the options from the last load operation performed.
23039      * @param {Object} options (optional) An object containing properties which may override the options
23040      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23041      * the most recently used options are reused).
23042      */
23043     reload : function(options){
23044         this.load(Roo.applyIf(options||{}, this.lastOptions));
23045     },
23046
23047     // private
23048     // Called as a callback by the Reader during a load operation.
23049     loadRecords : function(o, options, success){
23050         if(!o || success === false){
23051             if(success !== false){
23052                 this.fireEvent("load", this, [], options, o);
23053             }
23054             if(options.callback){
23055                 options.callback.call(options.scope || this, [], options, false);
23056             }
23057             return;
23058         }
23059         // if data returned failure - throw an exception.
23060         if (o.success === false) {
23061             // show a message if no listener is registered.
23062             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23063                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23064             }
23065             // loadmask wil be hooked into this..
23066             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23067             return;
23068         }
23069         var r = o.records, t = o.totalRecords || r.length;
23070         
23071         this.fireEvent("beforeloadadd", this, r, options, o);
23072         
23073         if(!options || options.add !== true){
23074             if(this.pruneModifiedRecords){
23075                 this.modified = [];
23076             }
23077             for(var i = 0, len = r.length; i < len; i++){
23078                 r[i].join(this);
23079             }
23080             if(this.snapshot){
23081                 this.data = this.snapshot;
23082                 delete this.snapshot;
23083             }
23084             this.data.clear();
23085             this.data.addAll(r);
23086             this.totalLength = t;
23087             this.applySort();
23088             this.fireEvent("datachanged", this);
23089         }else{
23090             this.totalLength = Math.max(t, this.data.length+r.length);
23091             this.add(r);
23092         }
23093         
23094         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23095                 
23096             var e = new Roo.data.Record({});
23097
23098             e.set(this.parent.displayField, this.parent.emptyTitle);
23099             e.set(this.parent.valueField, '');
23100
23101             this.insert(0, e);
23102         }
23103             
23104         this.fireEvent("load", this, r, options, o);
23105         if(options.callback){
23106             options.callback.call(options.scope || this, r, options, true);
23107         }
23108     },
23109
23110
23111     /**
23112      * Loads data from a passed data block. A Reader which understands the format of the data
23113      * must have been configured in the constructor.
23114      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23115      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23116      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23117      */
23118     loadData : function(o, append){
23119         var r = this.reader.readRecords(o);
23120         this.loadRecords(r, {add: append}, true);
23121     },
23122
23123     /**
23124      * Gets the number of cached records.
23125      * <p>
23126      * <em>If using paging, this may not be the total size of the dataset. If the data object
23127      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23128      * the data set size</em>
23129      */
23130     getCount : function(){
23131         return this.data.length || 0;
23132     },
23133
23134     /**
23135      * Gets the total number of records in the dataset as returned by the server.
23136      * <p>
23137      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23138      * the dataset size</em>
23139      */
23140     getTotalCount : function(){
23141         return this.totalLength || 0;
23142     },
23143
23144     /**
23145      * Returns the sort state of the Store as an object with two properties:
23146      * <pre><code>
23147  field {String} The name of the field by which the Records are sorted
23148  direction {String} The sort order, "ASC" or "DESC"
23149      * </code></pre>
23150      */
23151     getSortState : function(){
23152         return this.sortInfo;
23153     },
23154
23155     // private
23156     applySort : function(){
23157         if(this.sortInfo && !this.remoteSort){
23158             var s = this.sortInfo, f = s.field;
23159             var st = this.fields.get(f).sortType;
23160             var fn = function(r1, r2){
23161                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23162                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23163             };
23164             this.data.sort(s.direction, fn);
23165             if(this.snapshot && this.snapshot != this.data){
23166                 this.snapshot.sort(s.direction, fn);
23167             }
23168         }
23169     },
23170
23171     /**
23172      * Sets the default sort column and order to be used by the next load operation.
23173      * @param {String} fieldName The name of the field to sort by.
23174      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23175      */
23176     setDefaultSort : function(field, dir){
23177         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23178     },
23179
23180     /**
23181      * Sort the Records.
23182      * If remote sorting is used, the sort is performed on the server, and the cache is
23183      * reloaded. If local sorting is used, the cache is sorted internally.
23184      * @param {String} fieldName The name of the field to sort by.
23185      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23186      */
23187     sort : function(fieldName, dir){
23188         var f = this.fields.get(fieldName);
23189         if(!dir){
23190             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23191             
23192             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23193                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23194             }else{
23195                 dir = f.sortDir;
23196             }
23197         }
23198         this.sortToggle[f.name] = dir;
23199         this.sortInfo = {field: f.name, direction: dir};
23200         if(!this.remoteSort){
23201             this.applySort();
23202             this.fireEvent("datachanged", this);
23203         }else{
23204             this.load(this.lastOptions);
23205         }
23206     },
23207
23208     /**
23209      * Calls the specified function for each of the Records in the cache.
23210      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23211      * Returning <em>false</em> aborts and exits the iteration.
23212      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23213      */
23214     each : function(fn, scope){
23215         this.data.each(fn, scope);
23216     },
23217
23218     /**
23219      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23220      * (e.g., during paging).
23221      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23222      */
23223     getModifiedRecords : function(){
23224         return this.modified;
23225     },
23226
23227     // private
23228     createFilterFn : function(property, value, anyMatch){
23229         if(!value.exec){ // not a regex
23230             value = String(value);
23231             if(value.length == 0){
23232                 return false;
23233             }
23234             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23235         }
23236         return function(r){
23237             return value.test(r.data[property]);
23238         };
23239     },
23240
23241     /**
23242      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23243      * @param {String} property A field on your records
23244      * @param {Number} start The record index to start at (defaults to 0)
23245      * @param {Number} end The last record index to include (defaults to length - 1)
23246      * @return {Number} The sum
23247      */
23248     sum : function(property, start, end){
23249         var rs = this.data.items, v = 0;
23250         start = start || 0;
23251         end = (end || end === 0) ? end : rs.length-1;
23252
23253         for(var i = start; i <= end; i++){
23254             v += (rs[i].data[property] || 0);
23255         }
23256         return v;
23257     },
23258
23259     /**
23260      * Filter the records by a specified property.
23261      * @param {String} field A field on your records
23262      * @param {String/RegExp} value Either a string that the field
23263      * should start with or a RegExp to test against the field
23264      * @param {Boolean} anyMatch True to match any part not just the beginning
23265      */
23266     filter : function(property, value, anyMatch){
23267         var fn = this.createFilterFn(property, value, anyMatch);
23268         return fn ? this.filterBy(fn) : this.clearFilter();
23269     },
23270
23271     /**
23272      * Filter by a function. The specified function will be called with each
23273      * record in this data source. If the function returns true the record is included,
23274      * otherwise it is filtered.
23275      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23276      * @param {Object} scope (optional) The scope of the function (defaults to this)
23277      */
23278     filterBy : function(fn, scope){
23279         this.snapshot = this.snapshot || this.data;
23280         this.data = this.queryBy(fn, scope||this);
23281         this.fireEvent("datachanged", this);
23282     },
23283
23284     /**
23285      * Query the records by a specified property.
23286      * @param {String} field A field on your records
23287      * @param {String/RegExp} value Either a string that the field
23288      * should start with or a RegExp to test against the field
23289      * @param {Boolean} anyMatch True to match any part not just the beginning
23290      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23291      */
23292     query : function(property, value, anyMatch){
23293         var fn = this.createFilterFn(property, value, anyMatch);
23294         return fn ? this.queryBy(fn) : this.data.clone();
23295     },
23296
23297     /**
23298      * Query by a function. The specified function will be called with each
23299      * record in this data source. If the function returns true the record is included
23300      * in the results.
23301      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23302      * @param {Object} scope (optional) The scope of the function (defaults to this)
23303       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23304      **/
23305     queryBy : function(fn, scope){
23306         var data = this.snapshot || this.data;
23307         return data.filterBy(fn, scope||this);
23308     },
23309
23310     /**
23311      * Collects unique values for a particular dataIndex from this store.
23312      * @param {String} dataIndex The property to collect
23313      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23314      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23315      * @return {Array} An array of the unique values
23316      **/
23317     collect : function(dataIndex, allowNull, bypassFilter){
23318         var d = (bypassFilter === true && this.snapshot) ?
23319                 this.snapshot.items : this.data.items;
23320         var v, sv, r = [], l = {};
23321         for(var i = 0, len = d.length; i < len; i++){
23322             v = d[i].data[dataIndex];
23323             sv = String(v);
23324             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23325                 l[sv] = true;
23326                 r[r.length] = v;
23327             }
23328         }
23329         return r;
23330     },
23331
23332     /**
23333      * Revert to a view of the Record cache with no filtering applied.
23334      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23335      */
23336     clearFilter : function(suppressEvent){
23337         if(this.snapshot && this.snapshot != this.data){
23338             this.data = this.snapshot;
23339             delete this.snapshot;
23340             if(suppressEvent !== true){
23341                 this.fireEvent("datachanged", this);
23342             }
23343         }
23344     },
23345
23346     // private
23347     afterEdit : function(record){
23348         if(this.modified.indexOf(record) == -1){
23349             this.modified.push(record);
23350         }
23351         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23352     },
23353     
23354     // private
23355     afterReject : function(record){
23356         this.modified.remove(record);
23357         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23358     },
23359
23360     // private
23361     afterCommit : function(record){
23362         this.modified.remove(record);
23363         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23364     },
23365
23366     /**
23367      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23368      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23369      */
23370     commitChanges : function(){
23371         var m = this.modified.slice(0);
23372         this.modified = [];
23373         for(var i = 0, len = m.length; i < len; i++){
23374             m[i].commit();
23375         }
23376     },
23377
23378     /**
23379      * Cancel outstanding changes on all changed records.
23380      */
23381     rejectChanges : function(){
23382         var m = this.modified.slice(0);
23383         this.modified = [];
23384         for(var i = 0, len = m.length; i < len; i++){
23385             m[i].reject();
23386         }
23387     },
23388
23389     onMetaChange : function(meta, rtype, o){
23390         this.recordType = rtype;
23391         this.fields = rtype.prototype.fields;
23392         delete this.snapshot;
23393         this.sortInfo = meta.sortInfo || this.sortInfo;
23394         this.modified = [];
23395         this.fireEvent('metachange', this, this.reader.meta);
23396     },
23397     
23398     moveIndex : function(data, type)
23399     {
23400         var index = this.indexOf(data);
23401         
23402         var newIndex = index + type;
23403         
23404         this.remove(data);
23405         
23406         this.insert(newIndex, data);
23407         
23408     }
23409 });/*
23410  * Based on:
23411  * Ext JS Library 1.1.1
23412  * Copyright(c) 2006-2007, Ext JS, LLC.
23413  *
23414  * Originally Released Under LGPL - original licence link has changed is not relivant.
23415  *
23416  * Fork - LGPL
23417  * <script type="text/javascript">
23418  */
23419
23420 /**
23421  * @class Roo.data.SimpleStore
23422  * @extends Roo.data.Store
23423  * Small helper class to make creating Stores from Array data easier.
23424  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23425  * @cfg {Array} fields An array of field definition objects, or field name strings.
23426  * @cfg {Array} data The multi-dimensional array of data
23427  * @constructor
23428  * @param {Object} config
23429  */
23430 Roo.data.SimpleStore = function(config){
23431     Roo.data.SimpleStore.superclass.constructor.call(this, {
23432         isLocal : true,
23433         reader: new Roo.data.ArrayReader({
23434                 id: config.id
23435             },
23436             Roo.data.Record.create(config.fields)
23437         ),
23438         proxy : new Roo.data.MemoryProxy(config.data)
23439     });
23440     this.load();
23441 };
23442 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23443  * Based on:
23444  * Ext JS Library 1.1.1
23445  * Copyright(c) 2006-2007, Ext JS, LLC.
23446  *
23447  * Originally Released Under LGPL - original licence link has changed is not relivant.
23448  *
23449  * Fork - LGPL
23450  * <script type="text/javascript">
23451  */
23452
23453 /**
23454 /**
23455  * @extends Roo.data.Store
23456  * @class Roo.data.JsonStore
23457  * Small helper class to make creating Stores for JSON data easier. <br/>
23458 <pre><code>
23459 var store = new Roo.data.JsonStore({
23460     url: 'get-images.php',
23461     root: 'images',
23462     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23463 });
23464 </code></pre>
23465  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23466  * JsonReader and HttpProxy (unless inline data is provided).</b>
23467  * @cfg {Array} fields An array of field definition objects, or field name strings.
23468  * @constructor
23469  * @param {Object} config
23470  */
23471 Roo.data.JsonStore = function(c){
23472     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23473         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23474         reader: new Roo.data.JsonReader(c, c.fields)
23475     }));
23476 };
23477 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23478  * Based on:
23479  * Ext JS Library 1.1.1
23480  * Copyright(c) 2006-2007, Ext JS, LLC.
23481  *
23482  * Originally Released Under LGPL - original licence link has changed is not relivant.
23483  *
23484  * Fork - LGPL
23485  * <script type="text/javascript">
23486  */
23487
23488  
23489 Roo.data.Field = function(config){
23490     if(typeof config == "string"){
23491         config = {name: config};
23492     }
23493     Roo.apply(this, config);
23494     
23495     if(!this.type){
23496         this.type = "auto";
23497     }
23498     
23499     var st = Roo.data.SortTypes;
23500     // named sortTypes are supported, here we look them up
23501     if(typeof this.sortType == "string"){
23502         this.sortType = st[this.sortType];
23503     }
23504     
23505     // set default sortType for strings and dates
23506     if(!this.sortType){
23507         switch(this.type){
23508             case "string":
23509                 this.sortType = st.asUCString;
23510                 break;
23511             case "date":
23512                 this.sortType = st.asDate;
23513                 break;
23514             default:
23515                 this.sortType = st.none;
23516         }
23517     }
23518
23519     // define once
23520     var stripRe = /[\$,%]/g;
23521
23522     // prebuilt conversion function for this field, instead of
23523     // switching every time we're reading a value
23524     if(!this.convert){
23525         var cv, dateFormat = this.dateFormat;
23526         switch(this.type){
23527             case "":
23528             case "auto":
23529             case undefined:
23530                 cv = function(v){ return v; };
23531                 break;
23532             case "string":
23533                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23534                 break;
23535             case "int":
23536                 cv = function(v){
23537                     return v !== undefined && v !== null && v !== '' ?
23538                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23539                     };
23540                 break;
23541             case "float":
23542                 cv = function(v){
23543                     return v !== undefined && v !== null && v !== '' ?
23544                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23545                     };
23546                 break;
23547             case "bool":
23548             case "boolean":
23549                 cv = function(v){ return v === true || v === "true" || v == 1; };
23550                 break;
23551             case "date":
23552                 cv = function(v){
23553                     if(!v){
23554                         return '';
23555                     }
23556                     if(v instanceof Date){
23557                         return v;
23558                     }
23559                     if(dateFormat){
23560                         if(dateFormat == "timestamp"){
23561                             return new Date(v*1000);
23562                         }
23563                         return Date.parseDate(v, dateFormat);
23564                     }
23565                     var parsed = Date.parse(v);
23566                     return parsed ? new Date(parsed) : null;
23567                 };
23568              break;
23569             
23570         }
23571         this.convert = cv;
23572     }
23573 };
23574
23575 Roo.data.Field.prototype = {
23576     dateFormat: null,
23577     defaultValue: "",
23578     mapping: null,
23579     sortType : null,
23580     sortDir : "ASC"
23581 };/*
23582  * Based on:
23583  * Ext JS Library 1.1.1
23584  * Copyright(c) 2006-2007, Ext JS, LLC.
23585  *
23586  * Originally Released Under LGPL - original licence link has changed is not relivant.
23587  *
23588  * Fork - LGPL
23589  * <script type="text/javascript">
23590  */
23591  
23592 // Base class for reading structured data from a data source.  This class is intended to be
23593 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23594
23595 /**
23596  * @class Roo.data.DataReader
23597  * Base class for reading structured data from a data source.  This class is intended to be
23598  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23599  */
23600
23601 Roo.data.DataReader = function(meta, recordType){
23602     
23603     this.meta = meta;
23604     
23605     this.recordType = recordType instanceof Array ? 
23606         Roo.data.Record.create(recordType) : recordType;
23607 };
23608
23609 Roo.data.DataReader.prototype = {
23610      /**
23611      * Create an empty record
23612      * @param {Object} data (optional) - overlay some values
23613      * @return {Roo.data.Record} record created.
23614      */
23615     newRow :  function(d) {
23616         var da =  {};
23617         this.recordType.prototype.fields.each(function(c) {
23618             switch( c.type) {
23619                 case 'int' : da[c.name] = 0; break;
23620                 case 'date' : da[c.name] = new Date(); break;
23621                 case 'float' : da[c.name] = 0.0; break;
23622                 case 'boolean' : da[c.name] = false; break;
23623                 default : da[c.name] = ""; break;
23624             }
23625             
23626         });
23627         return new this.recordType(Roo.apply(da, d));
23628     }
23629     
23630 };/*
23631  * Based on:
23632  * Ext JS Library 1.1.1
23633  * Copyright(c) 2006-2007, Ext JS, LLC.
23634  *
23635  * Originally Released Under LGPL - original licence link has changed is not relivant.
23636  *
23637  * Fork - LGPL
23638  * <script type="text/javascript">
23639  */
23640
23641 /**
23642  * @class Roo.data.DataProxy
23643  * @extends Roo.data.Observable
23644  * This class is an abstract base class for implementations which provide retrieval of
23645  * unformatted data objects.<br>
23646  * <p>
23647  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23648  * (of the appropriate type which knows how to parse the data object) to provide a block of
23649  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23650  * <p>
23651  * Custom implementations must implement the load method as described in
23652  * {@link Roo.data.HttpProxy#load}.
23653  */
23654 Roo.data.DataProxy = function(){
23655     this.addEvents({
23656         /**
23657          * @event beforeload
23658          * Fires before a network request is made to retrieve a data object.
23659          * @param {Object} This DataProxy object.
23660          * @param {Object} params The params parameter to the load function.
23661          */
23662         beforeload : true,
23663         /**
23664          * @event load
23665          * Fires before the load method's callback is called.
23666          * @param {Object} This DataProxy object.
23667          * @param {Object} o The data object.
23668          * @param {Object} arg The callback argument object passed to the load function.
23669          */
23670         load : true,
23671         /**
23672          * @event loadexception
23673          * Fires if an Exception occurs during data retrieval.
23674          * @param {Object} This DataProxy object.
23675          * @param {Object} o The data object.
23676          * @param {Object} arg The callback argument object passed to the load function.
23677          * @param {Object} e The Exception.
23678          */
23679         loadexception : true
23680     });
23681     Roo.data.DataProxy.superclass.constructor.call(this);
23682 };
23683
23684 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23685
23686     /**
23687      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23688      */
23689 /*
23690  * Based on:
23691  * Ext JS Library 1.1.1
23692  * Copyright(c) 2006-2007, Ext JS, LLC.
23693  *
23694  * Originally Released Under LGPL - original licence link has changed is not relivant.
23695  *
23696  * Fork - LGPL
23697  * <script type="text/javascript">
23698  */
23699 /**
23700  * @class Roo.data.MemoryProxy
23701  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23702  * to the Reader when its load method is called.
23703  * @constructor
23704  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23705  */
23706 Roo.data.MemoryProxy = function(data){
23707     if (data.data) {
23708         data = data.data;
23709     }
23710     Roo.data.MemoryProxy.superclass.constructor.call(this);
23711     this.data = data;
23712 };
23713
23714 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23715     
23716     /**
23717      * Load data from the requested source (in this case an in-memory
23718      * data object passed to the constructor), read the data object into
23719      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23720      * process that block using the passed callback.
23721      * @param {Object} params This parameter is not used by the MemoryProxy class.
23722      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23723      * object into a block of Roo.data.Records.
23724      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23725      * The function must be passed <ul>
23726      * <li>The Record block object</li>
23727      * <li>The "arg" argument from the load function</li>
23728      * <li>A boolean success indicator</li>
23729      * </ul>
23730      * @param {Object} scope The scope in which to call the callback
23731      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23732      */
23733     load : function(params, reader, callback, scope, arg){
23734         params = params || {};
23735         var result;
23736         try {
23737             result = reader.readRecords(this.data);
23738         }catch(e){
23739             this.fireEvent("loadexception", this, arg, null, e);
23740             callback.call(scope, null, arg, false);
23741             return;
23742         }
23743         callback.call(scope, result, arg, true);
23744     },
23745     
23746     // private
23747     update : function(params, records){
23748         
23749     }
23750 });/*
23751  * Based on:
23752  * Ext JS Library 1.1.1
23753  * Copyright(c) 2006-2007, Ext JS, LLC.
23754  *
23755  * Originally Released Under LGPL - original licence link has changed is not relivant.
23756  *
23757  * Fork - LGPL
23758  * <script type="text/javascript">
23759  */
23760 /**
23761  * @class Roo.data.HttpProxy
23762  * @extends Roo.data.DataProxy
23763  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23764  * configured to reference a certain URL.<br><br>
23765  * <p>
23766  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23767  * from which the running page was served.<br><br>
23768  * <p>
23769  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23770  * <p>
23771  * Be aware that to enable the browser to parse an XML document, the server must set
23772  * the Content-Type header in the HTTP response to "text/xml".
23773  * @constructor
23774  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23775  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23776  * will be used to make the request.
23777  */
23778 Roo.data.HttpProxy = function(conn){
23779     Roo.data.HttpProxy.superclass.constructor.call(this);
23780     // is conn a conn config or a real conn?
23781     this.conn = conn;
23782     this.useAjax = !conn || !conn.events;
23783   
23784 };
23785
23786 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23787     // thse are take from connection...
23788     
23789     /**
23790      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23791      */
23792     /**
23793      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23794      * extra parameters to each request made by this object. (defaults to undefined)
23795      */
23796     /**
23797      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23798      *  to each request made by this object. (defaults to undefined)
23799      */
23800     /**
23801      * @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)
23802      */
23803     /**
23804      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23805      */
23806      /**
23807      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23808      * @type Boolean
23809      */
23810   
23811
23812     /**
23813      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23814      * @type Boolean
23815      */
23816     /**
23817      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23818      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23819      * a finer-grained basis than the DataProxy events.
23820      */
23821     getConnection : function(){
23822         return this.useAjax ? Roo.Ajax : this.conn;
23823     },
23824
23825     /**
23826      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23827      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23828      * process that block using the passed callback.
23829      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23830      * for the request to the remote server.
23831      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23832      * object into a block of Roo.data.Records.
23833      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23834      * The function must be passed <ul>
23835      * <li>The Record block object</li>
23836      * <li>The "arg" argument from the load function</li>
23837      * <li>A boolean success indicator</li>
23838      * </ul>
23839      * @param {Object} scope The scope in which to call the callback
23840      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23841      */
23842     load : function(params, reader, callback, scope, arg){
23843         if(this.fireEvent("beforeload", this, params) !== false){
23844             var  o = {
23845                 params : params || {},
23846                 request: {
23847                     callback : callback,
23848                     scope : scope,
23849                     arg : arg
23850                 },
23851                 reader: reader,
23852                 callback : this.loadResponse,
23853                 scope: this
23854             };
23855             if(this.useAjax){
23856                 Roo.applyIf(o, this.conn);
23857                 if(this.activeRequest){
23858                     Roo.Ajax.abort(this.activeRequest);
23859                 }
23860                 this.activeRequest = Roo.Ajax.request(o);
23861             }else{
23862                 this.conn.request(o);
23863             }
23864         }else{
23865             callback.call(scope||this, null, arg, false);
23866         }
23867     },
23868
23869     // private
23870     loadResponse : function(o, success, response){
23871         delete this.activeRequest;
23872         if(!success){
23873             this.fireEvent("loadexception", this, o, response);
23874             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23875             return;
23876         }
23877         var result;
23878         try {
23879             result = o.reader.read(response);
23880         }catch(e){
23881             this.fireEvent("loadexception", this, o, response, e);
23882             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23883             return;
23884         }
23885         
23886         this.fireEvent("load", this, o, o.request.arg);
23887         o.request.callback.call(o.request.scope, result, o.request.arg, true);
23888     },
23889
23890     // private
23891     update : function(dataSet){
23892
23893     },
23894
23895     // private
23896     updateResponse : function(dataSet){
23897
23898     }
23899 });/*
23900  * Based on:
23901  * Ext JS Library 1.1.1
23902  * Copyright(c) 2006-2007, Ext JS, LLC.
23903  *
23904  * Originally Released Under LGPL - original licence link has changed is not relivant.
23905  *
23906  * Fork - LGPL
23907  * <script type="text/javascript">
23908  */
23909
23910 /**
23911  * @class Roo.data.ScriptTagProxy
23912  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
23913  * other than the originating domain of the running page.<br><br>
23914  * <p>
23915  * <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
23916  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
23917  * <p>
23918  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
23919  * source code that is used as the source inside a &lt;script> tag.<br><br>
23920  * <p>
23921  * In order for the browser to process the returned data, the server must wrap the data object
23922  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
23923  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
23924  * depending on whether the callback name was passed:
23925  * <p>
23926  * <pre><code>
23927 boolean scriptTag = false;
23928 String cb = request.getParameter("callback");
23929 if (cb != null) {
23930     scriptTag = true;
23931     response.setContentType("text/javascript");
23932 } else {
23933     response.setContentType("application/x-json");
23934 }
23935 Writer out = response.getWriter();
23936 if (scriptTag) {
23937     out.write(cb + "(");
23938 }
23939 out.print(dataBlock.toJsonString());
23940 if (scriptTag) {
23941     out.write(");");
23942 }
23943 </pre></code>
23944  *
23945  * @constructor
23946  * @param {Object} config A configuration object.
23947  */
23948 Roo.data.ScriptTagProxy = function(config){
23949     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
23950     Roo.apply(this, config);
23951     this.head = document.getElementsByTagName("head")[0];
23952 };
23953
23954 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
23955
23956 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
23957     /**
23958      * @cfg {String} url The URL from which to request the data object.
23959      */
23960     /**
23961      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
23962      */
23963     timeout : 30000,
23964     /**
23965      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
23966      * the server the name of the callback function set up by the load call to process the returned data object.
23967      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
23968      * javascript output which calls this named function passing the data object as its only parameter.
23969      */
23970     callbackParam : "callback",
23971     /**
23972      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
23973      * name to the request.
23974      */
23975     nocache : true,
23976
23977     /**
23978      * Load data from the configured URL, read the data object into
23979      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23980      * process that block using the passed callback.
23981      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23982      * for the request to the remote server.
23983      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23984      * object into a block of Roo.data.Records.
23985      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23986      * The function must be passed <ul>
23987      * <li>The Record block object</li>
23988      * <li>The "arg" argument from the load function</li>
23989      * <li>A boolean success indicator</li>
23990      * </ul>
23991      * @param {Object} scope The scope in which to call the callback
23992      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23993      */
23994     load : function(params, reader, callback, scope, arg){
23995         if(this.fireEvent("beforeload", this, params) !== false){
23996
23997             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
23998
23999             var url = this.url;
24000             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24001             if(this.nocache){
24002                 url += "&_dc=" + (new Date().getTime());
24003             }
24004             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24005             var trans = {
24006                 id : transId,
24007                 cb : "stcCallback"+transId,
24008                 scriptId : "stcScript"+transId,
24009                 params : params,
24010                 arg : arg,
24011                 url : url,
24012                 callback : callback,
24013                 scope : scope,
24014                 reader : reader
24015             };
24016             var conn = this;
24017
24018             window[trans.cb] = function(o){
24019                 conn.handleResponse(o, trans);
24020             };
24021
24022             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24023
24024             if(this.autoAbort !== false){
24025                 this.abort();
24026             }
24027
24028             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24029
24030             var script = document.createElement("script");
24031             script.setAttribute("src", url);
24032             script.setAttribute("type", "text/javascript");
24033             script.setAttribute("id", trans.scriptId);
24034             this.head.appendChild(script);
24035
24036             this.trans = trans;
24037         }else{
24038             callback.call(scope||this, null, arg, false);
24039         }
24040     },
24041
24042     // private
24043     isLoading : function(){
24044         return this.trans ? true : false;
24045     },
24046
24047     /**
24048      * Abort the current server request.
24049      */
24050     abort : function(){
24051         if(this.isLoading()){
24052             this.destroyTrans(this.trans);
24053         }
24054     },
24055
24056     // private
24057     destroyTrans : function(trans, isLoaded){
24058         this.head.removeChild(document.getElementById(trans.scriptId));
24059         clearTimeout(trans.timeoutId);
24060         if(isLoaded){
24061             window[trans.cb] = undefined;
24062             try{
24063                 delete window[trans.cb];
24064             }catch(e){}
24065         }else{
24066             // if hasn't been loaded, wait for load to remove it to prevent script error
24067             window[trans.cb] = function(){
24068                 window[trans.cb] = undefined;
24069                 try{
24070                     delete window[trans.cb];
24071                 }catch(e){}
24072             };
24073         }
24074     },
24075
24076     // private
24077     handleResponse : function(o, trans){
24078         this.trans = false;
24079         this.destroyTrans(trans, true);
24080         var result;
24081         try {
24082             result = trans.reader.readRecords(o);
24083         }catch(e){
24084             this.fireEvent("loadexception", this, o, trans.arg, e);
24085             trans.callback.call(trans.scope||window, null, trans.arg, false);
24086             return;
24087         }
24088         this.fireEvent("load", this, o, trans.arg);
24089         trans.callback.call(trans.scope||window, result, trans.arg, true);
24090     },
24091
24092     // private
24093     handleFailure : function(trans){
24094         this.trans = false;
24095         this.destroyTrans(trans, false);
24096         this.fireEvent("loadexception", this, null, trans.arg);
24097         trans.callback.call(trans.scope||window, null, trans.arg, false);
24098     }
24099 });/*
24100  * Based on:
24101  * Ext JS Library 1.1.1
24102  * Copyright(c) 2006-2007, Ext JS, LLC.
24103  *
24104  * Originally Released Under LGPL - original licence link has changed is not relivant.
24105  *
24106  * Fork - LGPL
24107  * <script type="text/javascript">
24108  */
24109
24110 /**
24111  * @class Roo.data.JsonReader
24112  * @extends Roo.data.DataReader
24113  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24114  * based on mappings in a provided Roo.data.Record constructor.
24115  * 
24116  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24117  * in the reply previously. 
24118  * 
24119  * <p>
24120  * Example code:
24121  * <pre><code>
24122 var RecordDef = Roo.data.Record.create([
24123     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24124     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24125 ]);
24126 var myReader = new Roo.data.JsonReader({
24127     totalProperty: "results",    // The property which contains the total dataset size (optional)
24128     root: "rows",                // The property which contains an Array of row objects
24129     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24130 }, RecordDef);
24131 </code></pre>
24132  * <p>
24133  * This would consume a JSON file like this:
24134  * <pre><code>
24135 { 'results': 2, 'rows': [
24136     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24137     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24138 }
24139 </code></pre>
24140  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24141  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24142  * paged from the remote server.
24143  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24144  * @cfg {String} root name of the property which contains the Array of row objects.
24145  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24146  * @cfg {Array} fields Array of field definition objects
24147  * @constructor
24148  * Create a new JsonReader
24149  * @param {Object} meta Metadata configuration options
24150  * @param {Object} recordType Either an Array of field definition objects,
24151  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24152  */
24153 Roo.data.JsonReader = function(meta, recordType){
24154     
24155     meta = meta || {};
24156     // set some defaults:
24157     Roo.applyIf(meta, {
24158         totalProperty: 'total',
24159         successProperty : 'success',
24160         root : 'data',
24161         id : 'id'
24162     });
24163     
24164     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24165 };
24166 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24167     
24168     /**
24169      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24170      * Used by Store query builder to append _requestMeta to params.
24171      * 
24172      */
24173     metaFromRemote : false,
24174     /**
24175      * This method is only used by a DataProxy which has retrieved data from a remote server.
24176      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24177      * @return {Object} data A data block which is used by an Roo.data.Store object as
24178      * a cache of Roo.data.Records.
24179      */
24180     read : function(response){
24181         var json = response.responseText;
24182        
24183         var o = /* eval:var:o */ eval("("+json+")");
24184         if(!o) {
24185             throw {message: "JsonReader.read: Json object not found"};
24186         }
24187         
24188         if(o.metaData){
24189             
24190             delete this.ef;
24191             this.metaFromRemote = true;
24192             this.meta = o.metaData;
24193             this.recordType = Roo.data.Record.create(o.metaData.fields);
24194             this.onMetaChange(this.meta, this.recordType, o);
24195         }
24196         return this.readRecords(o);
24197     },
24198
24199     // private function a store will implement
24200     onMetaChange : function(meta, recordType, o){
24201
24202     },
24203
24204     /**
24205          * @ignore
24206          */
24207     simpleAccess: function(obj, subsc) {
24208         return obj[subsc];
24209     },
24210
24211         /**
24212          * @ignore
24213          */
24214     getJsonAccessor: function(){
24215         var re = /[\[\.]/;
24216         return function(expr) {
24217             try {
24218                 return(re.test(expr))
24219                     ? new Function("obj", "return obj." + expr)
24220                     : function(obj){
24221                         return obj[expr];
24222                     };
24223             } catch(e){}
24224             return Roo.emptyFn;
24225         };
24226     }(),
24227
24228     /**
24229      * Create a data block containing Roo.data.Records from an XML document.
24230      * @param {Object} o An object which contains an Array of row objects in the property specified
24231      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24232      * which contains the total size of the dataset.
24233      * @return {Object} data A data block which is used by an Roo.data.Store object as
24234      * a cache of Roo.data.Records.
24235      */
24236     readRecords : function(o){
24237         /**
24238          * After any data loads, the raw JSON data is available for further custom processing.
24239          * @type Object
24240          */
24241         this.o = o;
24242         var s = this.meta, Record = this.recordType,
24243             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24244
24245 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24246         if (!this.ef) {
24247             if(s.totalProperty) {
24248                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24249                 }
24250                 if(s.successProperty) {
24251                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24252                 }
24253                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24254                 if (s.id) {
24255                         var g = this.getJsonAccessor(s.id);
24256                         this.getId = function(rec) {
24257                                 var r = g(rec);  
24258                                 return (r === undefined || r === "") ? null : r;
24259                         };
24260                 } else {
24261                         this.getId = function(){return null;};
24262                 }
24263             this.ef = [];
24264             for(var jj = 0; jj < fl; jj++){
24265                 f = fi[jj];
24266                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24267                 this.ef[jj] = this.getJsonAccessor(map);
24268             }
24269         }
24270
24271         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24272         if(s.totalProperty){
24273             var vt = parseInt(this.getTotal(o), 10);
24274             if(!isNaN(vt)){
24275                 totalRecords = vt;
24276             }
24277         }
24278         if(s.successProperty){
24279             var vs = this.getSuccess(o);
24280             if(vs === false || vs === 'false'){
24281                 success = false;
24282             }
24283         }
24284         var records = [];
24285         for(var i = 0; i < c; i++){
24286                 var n = root[i];
24287             var values = {};
24288             var id = this.getId(n);
24289             for(var j = 0; j < fl; j++){
24290                 f = fi[j];
24291             var v = this.ef[j](n);
24292             if (!f.convert) {
24293                 Roo.log('missing convert for ' + f.name);
24294                 Roo.log(f);
24295                 continue;
24296             }
24297             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24298             }
24299             var record = new Record(values, id);
24300             record.json = n;
24301             records[i] = record;
24302         }
24303         return {
24304             raw : o,
24305             success : success,
24306             records : records,
24307             totalRecords : totalRecords
24308         };
24309     }
24310 });/*
24311  * Based on:
24312  * Ext JS Library 1.1.1
24313  * Copyright(c) 2006-2007, Ext JS, LLC.
24314  *
24315  * Originally Released Under LGPL - original licence link has changed is not relivant.
24316  *
24317  * Fork - LGPL
24318  * <script type="text/javascript">
24319  */
24320
24321 /**
24322  * @class Roo.data.XmlReader
24323  * @extends Roo.data.DataReader
24324  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24325  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24326  * <p>
24327  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24328  * header in the HTTP response must be set to "text/xml".</em>
24329  * <p>
24330  * Example code:
24331  * <pre><code>
24332 var RecordDef = Roo.data.Record.create([
24333    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24334    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24335 ]);
24336 var myReader = new Roo.data.XmlReader({
24337    totalRecords: "results", // The element which contains the total dataset size (optional)
24338    record: "row",           // The repeated element which contains row information
24339    id: "id"                 // The element within the row that provides an ID for the record (optional)
24340 }, RecordDef);
24341 </code></pre>
24342  * <p>
24343  * This would consume an XML file like this:
24344  * <pre><code>
24345 &lt;?xml?>
24346 &lt;dataset>
24347  &lt;results>2&lt;/results>
24348  &lt;row>
24349    &lt;id>1&lt;/id>
24350    &lt;name>Bill&lt;/name>
24351    &lt;occupation>Gardener&lt;/occupation>
24352  &lt;/row>
24353  &lt;row>
24354    &lt;id>2&lt;/id>
24355    &lt;name>Ben&lt;/name>
24356    &lt;occupation>Horticulturalist&lt;/occupation>
24357  &lt;/row>
24358 &lt;/dataset>
24359 </code></pre>
24360  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24361  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24362  * paged from the remote server.
24363  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24364  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24365  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24366  * a record identifier value.
24367  * @constructor
24368  * Create a new XmlReader
24369  * @param {Object} meta Metadata configuration options
24370  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24371  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24372  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24373  */
24374 Roo.data.XmlReader = function(meta, recordType){
24375     meta = meta || {};
24376     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24377 };
24378 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24379     /**
24380      * This method is only used by a DataProxy which has retrieved data from a remote server.
24381          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24382          * to contain a method called 'responseXML' that returns an XML document object.
24383      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24384      * a cache of Roo.data.Records.
24385      */
24386     read : function(response){
24387         var doc = response.responseXML;
24388         if(!doc) {
24389             throw {message: "XmlReader.read: XML Document not available"};
24390         }
24391         return this.readRecords(doc);
24392     },
24393
24394     /**
24395      * Create a data block containing Roo.data.Records from an XML document.
24396          * @param {Object} doc A parsed XML document.
24397      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24398      * a cache of Roo.data.Records.
24399      */
24400     readRecords : function(doc){
24401         /**
24402          * After any data loads/reads, the raw XML Document is available for further custom processing.
24403          * @type XMLDocument
24404          */
24405         this.xmlData = doc;
24406         var root = doc.documentElement || doc;
24407         var q = Roo.DomQuery;
24408         var recordType = this.recordType, fields = recordType.prototype.fields;
24409         var sid = this.meta.id;
24410         var totalRecords = 0, success = true;
24411         if(this.meta.totalRecords){
24412             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24413         }
24414         
24415         if(this.meta.success){
24416             var sv = q.selectValue(this.meta.success, root, true);
24417             success = sv !== false && sv !== 'false';
24418         }
24419         var records = [];
24420         var ns = q.select(this.meta.record, root);
24421         for(var i = 0, len = ns.length; i < len; i++) {
24422                 var n = ns[i];
24423                 var values = {};
24424                 var id = sid ? q.selectValue(sid, n) : undefined;
24425                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24426                     var f = fields.items[j];
24427                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24428                     v = f.convert(v);
24429                     values[f.name] = v;
24430                 }
24431                 var record = new recordType(values, id);
24432                 record.node = n;
24433                 records[records.length] = record;
24434             }
24435
24436             return {
24437                 success : success,
24438                 records : records,
24439                 totalRecords : totalRecords || records.length
24440             };
24441     }
24442 });/*
24443  * Based on:
24444  * Ext JS Library 1.1.1
24445  * Copyright(c) 2006-2007, Ext JS, LLC.
24446  *
24447  * Originally Released Under LGPL - original licence link has changed is not relivant.
24448  *
24449  * Fork - LGPL
24450  * <script type="text/javascript">
24451  */
24452
24453 /**
24454  * @class Roo.data.ArrayReader
24455  * @extends Roo.data.DataReader
24456  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24457  * Each element of that Array represents a row of data fields. The
24458  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24459  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24460  * <p>
24461  * Example code:.
24462  * <pre><code>
24463 var RecordDef = Roo.data.Record.create([
24464     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24465     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24466 ]);
24467 var myReader = new Roo.data.ArrayReader({
24468     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24469 }, RecordDef);
24470 </code></pre>
24471  * <p>
24472  * This would consume an Array like this:
24473  * <pre><code>
24474 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24475   </code></pre>
24476  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24477  * @constructor
24478  * Create a new JsonReader
24479  * @param {Object} meta Metadata configuration options.
24480  * @param {Object} recordType Either an Array of field definition objects
24481  * as specified to {@link Roo.data.Record#create},
24482  * or an {@link Roo.data.Record} object
24483  * created using {@link Roo.data.Record#create}.
24484  */
24485 Roo.data.ArrayReader = function(meta, recordType){
24486     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24487 };
24488
24489 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24490     /**
24491      * Create a data block containing Roo.data.Records from an XML document.
24492      * @param {Object} o An Array of row objects which represents the dataset.
24493      * @return {Object} data A data block which is used by an Roo.data.Store object as
24494      * a cache of Roo.data.Records.
24495      */
24496     readRecords : function(o){
24497         var sid = this.meta ? this.meta.id : null;
24498         var recordType = this.recordType, fields = recordType.prototype.fields;
24499         var records = [];
24500         var root = o;
24501             for(var i = 0; i < root.length; i++){
24502                     var n = root[i];
24503                 var values = {};
24504                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24505                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24506                 var f = fields.items[j];
24507                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24508                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24509                 v = f.convert(v);
24510                 values[f.name] = v;
24511             }
24512                 var record = new recordType(values, id);
24513                 record.json = n;
24514                 records[records.length] = record;
24515             }
24516             return {
24517                 records : records,
24518                 totalRecords : records.length
24519             };
24520     }
24521 });/*
24522  * Based on:
24523  * Ext JS Library 1.1.1
24524  * Copyright(c) 2006-2007, Ext JS, LLC.
24525  *
24526  * Originally Released Under LGPL - original licence link has changed is not relivant.
24527  *
24528  * Fork - LGPL
24529  * <script type="text/javascript">
24530  */
24531
24532
24533 /**
24534  * @class Roo.data.Tree
24535  * @extends Roo.util.Observable
24536  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24537  * in the tree have most standard DOM functionality.
24538  * @constructor
24539  * @param {Node} root (optional) The root node
24540  */
24541 Roo.data.Tree = function(root){
24542    this.nodeHash = {};
24543    /**
24544     * The root node for this tree
24545     * @type Node
24546     */
24547    this.root = null;
24548    if(root){
24549        this.setRootNode(root);
24550    }
24551    this.addEvents({
24552        /**
24553         * @event append
24554         * Fires when a new child node is appended to a node in this tree.
24555         * @param {Tree} tree The owner tree
24556         * @param {Node} parent The parent node
24557         * @param {Node} node The newly appended node
24558         * @param {Number} index The index of the newly appended node
24559         */
24560        "append" : true,
24561        /**
24562         * @event remove
24563         * Fires when a child node is removed from a node in this tree.
24564         * @param {Tree} tree The owner tree
24565         * @param {Node} parent The parent node
24566         * @param {Node} node The child node removed
24567         */
24568        "remove" : true,
24569        /**
24570         * @event move
24571         * Fires when a node is moved to a new location in the tree
24572         * @param {Tree} tree The owner tree
24573         * @param {Node} node The node moved
24574         * @param {Node} oldParent The old parent of this node
24575         * @param {Node} newParent The new parent of this node
24576         * @param {Number} index The index it was moved to
24577         */
24578        "move" : true,
24579        /**
24580         * @event insert
24581         * Fires when a new child node is inserted in a node in this tree.
24582         * @param {Tree} tree The owner tree
24583         * @param {Node} parent The parent node
24584         * @param {Node} node The child node inserted
24585         * @param {Node} refNode The child node the node was inserted before
24586         */
24587        "insert" : true,
24588        /**
24589         * @event beforeappend
24590         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24591         * @param {Tree} tree The owner tree
24592         * @param {Node} parent The parent node
24593         * @param {Node} node The child node to be appended
24594         */
24595        "beforeappend" : true,
24596        /**
24597         * @event beforeremove
24598         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24599         * @param {Tree} tree The owner tree
24600         * @param {Node} parent The parent node
24601         * @param {Node} node The child node to be removed
24602         */
24603        "beforeremove" : true,
24604        /**
24605         * @event beforemove
24606         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24607         * @param {Tree} tree The owner tree
24608         * @param {Node} node The node being moved
24609         * @param {Node} oldParent The parent of the node
24610         * @param {Node} newParent The new parent the node is moving to
24611         * @param {Number} index The index it is being moved to
24612         */
24613        "beforemove" : true,
24614        /**
24615         * @event beforeinsert
24616         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24617         * @param {Tree} tree The owner tree
24618         * @param {Node} parent The parent node
24619         * @param {Node} node The child node to be inserted
24620         * @param {Node} refNode The child node the node is being inserted before
24621         */
24622        "beforeinsert" : true
24623    });
24624
24625     Roo.data.Tree.superclass.constructor.call(this);
24626 };
24627
24628 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24629     pathSeparator: "/",
24630
24631     proxyNodeEvent : function(){
24632         return this.fireEvent.apply(this, arguments);
24633     },
24634
24635     /**
24636      * Returns the root node for this tree.
24637      * @return {Node}
24638      */
24639     getRootNode : function(){
24640         return this.root;
24641     },
24642
24643     /**
24644      * Sets the root node for this tree.
24645      * @param {Node} node
24646      * @return {Node}
24647      */
24648     setRootNode : function(node){
24649         this.root = node;
24650         node.ownerTree = this;
24651         node.isRoot = true;
24652         this.registerNode(node);
24653         return node;
24654     },
24655
24656     /**
24657      * Gets a node in this tree by its id.
24658      * @param {String} id
24659      * @return {Node}
24660      */
24661     getNodeById : function(id){
24662         return this.nodeHash[id];
24663     },
24664
24665     registerNode : function(node){
24666         this.nodeHash[node.id] = node;
24667     },
24668
24669     unregisterNode : function(node){
24670         delete this.nodeHash[node.id];
24671     },
24672
24673     toString : function(){
24674         return "[Tree"+(this.id?" "+this.id:"")+"]";
24675     }
24676 });
24677
24678 /**
24679  * @class Roo.data.Node
24680  * @extends Roo.util.Observable
24681  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24682  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24683  * @constructor
24684  * @param {Object} attributes The attributes/config for the node
24685  */
24686 Roo.data.Node = function(attributes){
24687     /**
24688      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24689      * @type {Object}
24690      */
24691     this.attributes = attributes || {};
24692     this.leaf = this.attributes.leaf;
24693     /**
24694      * The node id. @type String
24695      */
24696     this.id = this.attributes.id;
24697     if(!this.id){
24698         this.id = Roo.id(null, "ynode-");
24699         this.attributes.id = this.id;
24700     }
24701      
24702     
24703     /**
24704      * All child nodes of this node. @type Array
24705      */
24706     this.childNodes = [];
24707     if(!this.childNodes.indexOf){ // indexOf is a must
24708         this.childNodes.indexOf = function(o){
24709             for(var i = 0, len = this.length; i < len; i++){
24710                 if(this[i] == o) {
24711                     return i;
24712                 }
24713             }
24714             return -1;
24715         };
24716     }
24717     /**
24718      * The parent node for this node. @type Node
24719      */
24720     this.parentNode = null;
24721     /**
24722      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24723      */
24724     this.firstChild = null;
24725     /**
24726      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24727      */
24728     this.lastChild = null;
24729     /**
24730      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24731      */
24732     this.previousSibling = null;
24733     /**
24734      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24735      */
24736     this.nextSibling = null;
24737
24738     this.addEvents({
24739        /**
24740         * @event append
24741         * Fires when a new child node is appended
24742         * @param {Tree} tree The owner tree
24743         * @param {Node} this This node
24744         * @param {Node} node The newly appended node
24745         * @param {Number} index The index of the newly appended node
24746         */
24747        "append" : true,
24748        /**
24749         * @event remove
24750         * Fires when a child node is removed
24751         * @param {Tree} tree The owner tree
24752         * @param {Node} this This node
24753         * @param {Node} node The removed node
24754         */
24755        "remove" : true,
24756        /**
24757         * @event move
24758         * Fires when this node is moved to a new location in the tree
24759         * @param {Tree} tree The owner tree
24760         * @param {Node} this This node
24761         * @param {Node} oldParent The old parent of this node
24762         * @param {Node} newParent The new parent of this node
24763         * @param {Number} index The index it was moved to
24764         */
24765        "move" : true,
24766        /**
24767         * @event insert
24768         * Fires when a new child node is inserted.
24769         * @param {Tree} tree The owner tree
24770         * @param {Node} this This node
24771         * @param {Node} node The child node inserted
24772         * @param {Node} refNode The child node the node was inserted before
24773         */
24774        "insert" : true,
24775        /**
24776         * @event beforeappend
24777         * Fires before a new child is appended, return false to cancel the append.
24778         * @param {Tree} tree The owner tree
24779         * @param {Node} this This node
24780         * @param {Node} node The child node to be appended
24781         */
24782        "beforeappend" : true,
24783        /**
24784         * @event beforeremove
24785         * Fires before a child is removed, return false to cancel the remove.
24786         * @param {Tree} tree The owner tree
24787         * @param {Node} this This node
24788         * @param {Node} node The child node to be removed
24789         */
24790        "beforeremove" : true,
24791        /**
24792         * @event beforemove
24793         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24794         * @param {Tree} tree The owner tree
24795         * @param {Node} this This node
24796         * @param {Node} oldParent The parent of this node
24797         * @param {Node} newParent The new parent this node is moving to
24798         * @param {Number} index The index it is being moved to
24799         */
24800        "beforemove" : true,
24801        /**
24802         * @event beforeinsert
24803         * Fires before a new child is inserted, return false to cancel the insert.
24804         * @param {Tree} tree The owner tree
24805         * @param {Node} this This node
24806         * @param {Node} node The child node to be inserted
24807         * @param {Node} refNode The child node the node is being inserted before
24808         */
24809        "beforeinsert" : true
24810    });
24811     this.listeners = this.attributes.listeners;
24812     Roo.data.Node.superclass.constructor.call(this);
24813 };
24814
24815 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24816     fireEvent : function(evtName){
24817         // first do standard event for this node
24818         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24819             return false;
24820         }
24821         // then bubble it up to the tree if the event wasn't cancelled
24822         var ot = this.getOwnerTree();
24823         if(ot){
24824             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24825                 return false;
24826             }
24827         }
24828         return true;
24829     },
24830
24831     /**
24832      * Returns true if this node is a leaf
24833      * @return {Boolean}
24834      */
24835     isLeaf : function(){
24836         return this.leaf === true;
24837     },
24838
24839     // private
24840     setFirstChild : function(node){
24841         this.firstChild = node;
24842     },
24843
24844     //private
24845     setLastChild : function(node){
24846         this.lastChild = node;
24847     },
24848
24849
24850     /**
24851      * Returns true if this node is the last child of its parent
24852      * @return {Boolean}
24853      */
24854     isLast : function(){
24855        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24856     },
24857
24858     /**
24859      * Returns true if this node is the first child of its parent
24860      * @return {Boolean}
24861      */
24862     isFirst : function(){
24863        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24864     },
24865
24866     hasChildNodes : function(){
24867         return !this.isLeaf() && this.childNodes.length > 0;
24868     },
24869
24870     /**
24871      * Insert node(s) as the last child node of this node.
24872      * @param {Node/Array} node The node or Array of nodes to append
24873      * @return {Node} The appended node if single append, or null if an array was passed
24874      */
24875     appendChild : function(node){
24876         var multi = false;
24877         if(node instanceof Array){
24878             multi = node;
24879         }else if(arguments.length > 1){
24880             multi = arguments;
24881         }
24882         // if passed an array or multiple args do them one by one
24883         if(multi){
24884             for(var i = 0, len = multi.length; i < len; i++) {
24885                 this.appendChild(multi[i]);
24886             }
24887         }else{
24888             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
24889                 return false;
24890             }
24891             var index = this.childNodes.length;
24892             var oldParent = node.parentNode;
24893             // it's a move, make sure we move it cleanly
24894             if(oldParent){
24895                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
24896                     return false;
24897                 }
24898                 oldParent.removeChild(node);
24899             }
24900             index = this.childNodes.length;
24901             if(index == 0){
24902                 this.setFirstChild(node);
24903             }
24904             this.childNodes.push(node);
24905             node.parentNode = this;
24906             var ps = this.childNodes[index-1];
24907             if(ps){
24908                 node.previousSibling = ps;
24909                 ps.nextSibling = node;
24910             }else{
24911                 node.previousSibling = null;
24912             }
24913             node.nextSibling = null;
24914             this.setLastChild(node);
24915             node.setOwnerTree(this.getOwnerTree());
24916             this.fireEvent("append", this.ownerTree, this, node, index);
24917             if(oldParent){
24918                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
24919             }
24920             return node;
24921         }
24922     },
24923
24924     /**
24925      * Removes a child node from this node.
24926      * @param {Node} node The node to remove
24927      * @return {Node} The removed node
24928      */
24929     removeChild : function(node){
24930         var index = this.childNodes.indexOf(node);
24931         if(index == -1){
24932             return false;
24933         }
24934         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
24935             return false;
24936         }
24937
24938         // remove it from childNodes collection
24939         this.childNodes.splice(index, 1);
24940
24941         // update siblings
24942         if(node.previousSibling){
24943             node.previousSibling.nextSibling = node.nextSibling;
24944         }
24945         if(node.nextSibling){
24946             node.nextSibling.previousSibling = node.previousSibling;
24947         }
24948
24949         // update child refs
24950         if(this.firstChild == node){
24951             this.setFirstChild(node.nextSibling);
24952         }
24953         if(this.lastChild == node){
24954             this.setLastChild(node.previousSibling);
24955         }
24956
24957         node.setOwnerTree(null);
24958         // clear any references from the node
24959         node.parentNode = null;
24960         node.previousSibling = null;
24961         node.nextSibling = null;
24962         this.fireEvent("remove", this.ownerTree, this, node);
24963         return node;
24964     },
24965
24966     /**
24967      * Inserts the first node before the second node in this nodes childNodes collection.
24968      * @param {Node} node The node to insert
24969      * @param {Node} refNode The node to insert before (if null the node is appended)
24970      * @return {Node} The inserted node
24971      */
24972     insertBefore : function(node, refNode){
24973         if(!refNode){ // like standard Dom, refNode can be null for append
24974             return this.appendChild(node);
24975         }
24976         // nothing to do
24977         if(node == refNode){
24978             return false;
24979         }
24980
24981         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
24982             return false;
24983         }
24984         var index = this.childNodes.indexOf(refNode);
24985         var oldParent = node.parentNode;
24986         var refIndex = index;
24987
24988         // when moving internally, indexes will change after remove
24989         if(oldParent == this && this.childNodes.indexOf(node) < index){
24990             refIndex--;
24991         }
24992
24993         // it's a move, make sure we move it cleanly
24994         if(oldParent){
24995             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
24996                 return false;
24997             }
24998             oldParent.removeChild(node);
24999         }
25000         if(refIndex == 0){
25001             this.setFirstChild(node);
25002         }
25003         this.childNodes.splice(refIndex, 0, node);
25004         node.parentNode = this;
25005         var ps = this.childNodes[refIndex-1];
25006         if(ps){
25007             node.previousSibling = ps;
25008             ps.nextSibling = node;
25009         }else{
25010             node.previousSibling = null;
25011         }
25012         node.nextSibling = refNode;
25013         refNode.previousSibling = node;
25014         node.setOwnerTree(this.getOwnerTree());
25015         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25016         if(oldParent){
25017             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25018         }
25019         return node;
25020     },
25021
25022     /**
25023      * Returns the child node at the specified index.
25024      * @param {Number} index
25025      * @return {Node}
25026      */
25027     item : function(index){
25028         return this.childNodes[index];
25029     },
25030
25031     /**
25032      * Replaces one child node in this node with another.
25033      * @param {Node} newChild The replacement node
25034      * @param {Node} oldChild The node to replace
25035      * @return {Node} The replaced node
25036      */
25037     replaceChild : function(newChild, oldChild){
25038         this.insertBefore(newChild, oldChild);
25039         this.removeChild(oldChild);
25040         return oldChild;
25041     },
25042
25043     /**
25044      * Returns the index of a child node
25045      * @param {Node} node
25046      * @return {Number} The index of the node or -1 if it was not found
25047      */
25048     indexOf : function(child){
25049         return this.childNodes.indexOf(child);
25050     },
25051
25052     /**
25053      * Returns the tree this node is in.
25054      * @return {Tree}
25055      */
25056     getOwnerTree : function(){
25057         // if it doesn't have one, look for one
25058         if(!this.ownerTree){
25059             var p = this;
25060             while(p){
25061                 if(p.ownerTree){
25062                     this.ownerTree = p.ownerTree;
25063                     break;
25064                 }
25065                 p = p.parentNode;
25066             }
25067         }
25068         return this.ownerTree;
25069     },
25070
25071     /**
25072      * Returns depth of this node (the root node has a depth of 0)
25073      * @return {Number}
25074      */
25075     getDepth : function(){
25076         var depth = 0;
25077         var p = this;
25078         while(p.parentNode){
25079             ++depth;
25080             p = p.parentNode;
25081         }
25082         return depth;
25083     },
25084
25085     // private
25086     setOwnerTree : function(tree){
25087         // if it's move, we need to update everyone
25088         if(tree != this.ownerTree){
25089             if(this.ownerTree){
25090                 this.ownerTree.unregisterNode(this);
25091             }
25092             this.ownerTree = tree;
25093             var cs = this.childNodes;
25094             for(var i = 0, len = cs.length; i < len; i++) {
25095                 cs[i].setOwnerTree(tree);
25096             }
25097             if(tree){
25098                 tree.registerNode(this);
25099             }
25100         }
25101     },
25102
25103     /**
25104      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25105      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25106      * @return {String} The path
25107      */
25108     getPath : function(attr){
25109         attr = attr || "id";
25110         var p = this.parentNode;
25111         var b = [this.attributes[attr]];
25112         while(p){
25113             b.unshift(p.attributes[attr]);
25114             p = p.parentNode;
25115         }
25116         var sep = this.getOwnerTree().pathSeparator;
25117         return sep + b.join(sep);
25118     },
25119
25120     /**
25121      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25122      * function call will be the scope provided or the current node. The arguments to the function
25123      * will be the args provided or the current node. If the function returns false at any point,
25124      * the bubble is stopped.
25125      * @param {Function} fn The function to call
25126      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25127      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25128      */
25129     bubble : function(fn, scope, args){
25130         var p = this;
25131         while(p){
25132             if(fn.call(scope || p, args || p) === false){
25133                 break;
25134             }
25135             p = p.parentNode;
25136         }
25137     },
25138
25139     /**
25140      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25141      * function call will be the scope provided or the current node. The arguments to the function
25142      * will be the args provided or the current node. If the function returns false at any point,
25143      * the cascade is stopped on that branch.
25144      * @param {Function} fn The function to call
25145      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25146      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25147      */
25148     cascade : function(fn, scope, args){
25149         if(fn.call(scope || this, args || this) !== false){
25150             var cs = this.childNodes;
25151             for(var i = 0, len = cs.length; i < len; i++) {
25152                 cs[i].cascade(fn, scope, args);
25153             }
25154         }
25155     },
25156
25157     /**
25158      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25159      * function call will be the scope provided or the current node. The arguments to the function
25160      * will be the args provided or the current node. If the function returns false at any point,
25161      * the iteration stops.
25162      * @param {Function} fn The function to call
25163      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25164      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25165      */
25166     eachChild : function(fn, scope, args){
25167         var cs = this.childNodes;
25168         for(var i = 0, len = cs.length; i < len; i++) {
25169                 if(fn.call(scope || this, args || cs[i]) === false){
25170                     break;
25171                 }
25172         }
25173     },
25174
25175     /**
25176      * Finds the first child that has the attribute with the specified value.
25177      * @param {String} attribute The attribute name
25178      * @param {Mixed} value The value to search for
25179      * @return {Node} The found child or null if none was found
25180      */
25181     findChild : function(attribute, value){
25182         var cs = this.childNodes;
25183         for(var i = 0, len = cs.length; i < len; i++) {
25184                 if(cs[i].attributes[attribute] == value){
25185                     return cs[i];
25186                 }
25187         }
25188         return null;
25189     },
25190
25191     /**
25192      * Finds the first child by a custom function. The child matches if the function passed
25193      * returns true.
25194      * @param {Function} fn
25195      * @param {Object} scope (optional)
25196      * @return {Node} The found child or null if none was found
25197      */
25198     findChildBy : function(fn, scope){
25199         var cs = this.childNodes;
25200         for(var i = 0, len = cs.length; i < len; i++) {
25201                 if(fn.call(scope||cs[i], cs[i]) === true){
25202                     return cs[i];
25203                 }
25204         }
25205         return null;
25206     },
25207
25208     /**
25209      * Sorts this nodes children using the supplied sort function
25210      * @param {Function} fn
25211      * @param {Object} scope (optional)
25212      */
25213     sort : function(fn, scope){
25214         var cs = this.childNodes;
25215         var len = cs.length;
25216         if(len > 0){
25217             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25218             cs.sort(sortFn);
25219             for(var i = 0; i < len; i++){
25220                 var n = cs[i];
25221                 n.previousSibling = cs[i-1];
25222                 n.nextSibling = cs[i+1];
25223                 if(i == 0){
25224                     this.setFirstChild(n);
25225                 }
25226                 if(i == len-1){
25227                     this.setLastChild(n);
25228                 }
25229             }
25230         }
25231     },
25232
25233     /**
25234      * Returns true if this node is an ancestor (at any point) of the passed node.
25235      * @param {Node} node
25236      * @return {Boolean}
25237      */
25238     contains : function(node){
25239         return node.isAncestor(this);
25240     },
25241
25242     /**
25243      * Returns true if the passed node is an ancestor (at any point) of this node.
25244      * @param {Node} node
25245      * @return {Boolean}
25246      */
25247     isAncestor : function(node){
25248         var p = this.parentNode;
25249         while(p){
25250             if(p == node){
25251                 return true;
25252             }
25253             p = p.parentNode;
25254         }
25255         return false;
25256     },
25257
25258     toString : function(){
25259         return "[Node"+(this.id?" "+this.id:"")+"]";
25260     }
25261 });/*
25262  * Based on:
25263  * Ext JS Library 1.1.1
25264  * Copyright(c) 2006-2007, Ext JS, LLC.
25265  *
25266  * Originally Released Under LGPL - original licence link has changed is not relivant.
25267  *
25268  * Fork - LGPL
25269  * <script type="text/javascript">
25270  */
25271  (function(){ 
25272 /**
25273  * @class Roo.Layer
25274  * @extends Roo.Element
25275  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25276  * automatic maintaining of shadow/shim positions.
25277  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25278  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25279  * you can pass a string with a CSS class name. False turns off the shadow.
25280  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25281  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25282  * @cfg {String} cls CSS class to add to the element
25283  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25284  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25285  * @constructor
25286  * @param {Object} config An object with config options.
25287  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25288  */
25289
25290 Roo.Layer = function(config, existingEl){
25291     config = config || {};
25292     var dh = Roo.DomHelper;
25293     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25294     if(existingEl){
25295         this.dom = Roo.getDom(existingEl);
25296     }
25297     if(!this.dom){
25298         var o = config.dh || {tag: "div", cls: "x-layer"};
25299         this.dom = dh.append(pel, o);
25300     }
25301     if(config.cls){
25302         this.addClass(config.cls);
25303     }
25304     this.constrain = config.constrain !== false;
25305     this.visibilityMode = Roo.Element.VISIBILITY;
25306     if(config.id){
25307         this.id = this.dom.id = config.id;
25308     }else{
25309         this.id = Roo.id(this.dom);
25310     }
25311     this.zindex = config.zindex || this.getZIndex();
25312     this.position("absolute", this.zindex);
25313     if(config.shadow){
25314         this.shadowOffset = config.shadowOffset || 4;
25315         this.shadow = new Roo.Shadow({
25316             offset : this.shadowOffset,
25317             mode : config.shadow
25318         });
25319     }else{
25320         this.shadowOffset = 0;
25321     }
25322     this.useShim = config.shim !== false && Roo.useShims;
25323     this.useDisplay = config.useDisplay;
25324     this.hide();
25325 };
25326
25327 var supr = Roo.Element.prototype;
25328
25329 // shims are shared among layer to keep from having 100 iframes
25330 var shims = [];
25331
25332 Roo.extend(Roo.Layer, Roo.Element, {
25333
25334     getZIndex : function(){
25335         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25336     },
25337
25338     getShim : function(){
25339         if(!this.useShim){
25340             return null;
25341         }
25342         if(this.shim){
25343             return this.shim;
25344         }
25345         var shim = shims.shift();
25346         if(!shim){
25347             shim = this.createShim();
25348             shim.enableDisplayMode('block');
25349             shim.dom.style.display = 'none';
25350             shim.dom.style.visibility = 'visible';
25351         }
25352         var pn = this.dom.parentNode;
25353         if(shim.dom.parentNode != pn){
25354             pn.insertBefore(shim.dom, this.dom);
25355         }
25356         shim.setStyle('z-index', this.getZIndex()-2);
25357         this.shim = shim;
25358         return shim;
25359     },
25360
25361     hideShim : function(){
25362         if(this.shim){
25363             this.shim.setDisplayed(false);
25364             shims.push(this.shim);
25365             delete this.shim;
25366         }
25367     },
25368
25369     disableShadow : function(){
25370         if(this.shadow){
25371             this.shadowDisabled = true;
25372             this.shadow.hide();
25373             this.lastShadowOffset = this.shadowOffset;
25374             this.shadowOffset = 0;
25375         }
25376     },
25377
25378     enableShadow : function(show){
25379         if(this.shadow){
25380             this.shadowDisabled = false;
25381             this.shadowOffset = this.lastShadowOffset;
25382             delete this.lastShadowOffset;
25383             if(show){
25384                 this.sync(true);
25385             }
25386         }
25387     },
25388
25389     // private
25390     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25391     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25392     sync : function(doShow){
25393         var sw = this.shadow;
25394         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25395             var sh = this.getShim();
25396
25397             var w = this.getWidth(),
25398                 h = this.getHeight();
25399
25400             var l = this.getLeft(true),
25401                 t = this.getTop(true);
25402
25403             if(sw && !this.shadowDisabled){
25404                 if(doShow && !sw.isVisible()){
25405                     sw.show(this);
25406                 }else{
25407                     sw.realign(l, t, w, h);
25408                 }
25409                 if(sh){
25410                     if(doShow){
25411                        sh.show();
25412                     }
25413                     // fit the shim behind the shadow, so it is shimmed too
25414                     var a = sw.adjusts, s = sh.dom.style;
25415                     s.left = (Math.min(l, l+a.l))+"px";
25416                     s.top = (Math.min(t, t+a.t))+"px";
25417                     s.width = (w+a.w)+"px";
25418                     s.height = (h+a.h)+"px";
25419                 }
25420             }else if(sh){
25421                 if(doShow){
25422                    sh.show();
25423                 }
25424                 sh.setSize(w, h);
25425                 sh.setLeftTop(l, t);
25426             }
25427             
25428         }
25429     },
25430
25431     // private
25432     destroy : function(){
25433         this.hideShim();
25434         if(this.shadow){
25435             this.shadow.hide();
25436         }
25437         this.removeAllListeners();
25438         var pn = this.dom.parentNode;
25439         if(pn){
25440             pn.removeChild(this.dom);
25441         }
25442         Roo.Element.uncache(this.id);
25443     },
25444
25445     remove : function(){
25446         this.destroy();
25447     },
25448
25449     // private
25450     beginUpdate : function(){
25451         this.updating = true;
25452     },
25453
25454     // private
25455     endUpdate : function(){
25456         this.updating = false;
25457         this.sync(true);
25458     },
25459
25460     // private
25461     hideUnders : function(negOffset){
25462         if(this.shadow){
25463             this.shadow.hide();
25464         }
25465         this.hideShim();
25466     },
25467
25468     // private
25469     constrainXY : function(){
25470         if(this.constrain){
25471             var vw = Roo.lib.Dom.getViewWidth(),
25472                 vh = Roo.lib.Dom.getViewHeight();
25473             var s = Roo.get(document).getScroll();
25474
25475             var xy = this.getXY();
25476             var x = xy[0], y = xy[1];   
25477             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25478             // only move it if it needs it
25479             var moved = false;
25480             // first validate right/bottom
25481             if((x + w) > vw+s.left){
25482                 x = vw - w - this.shadowOffset;
25483                 moved = true;
25484             }
25485             if((y + h) > vh+s.top){
25486                 y = vh - h - this.shadowOffset;
25487                 moved = true;
25488             }
25489             // then make sure top/left isn't negative
25490             if(x < s.left){
25491                 x = s.left;
25492                 moved = true;
25493             }
25494             if(y < s.top){
25495                 y = s.top;
25496                 moved = true;
25497             }
25498             if(moved){
25499                 if(this.avoidY){
25500                     var ay = this.avoidY;
25501                     if(y <= ay && (y+h) >= ay){
25502                         y = ay-h-5;   
25503                     }
25504                 }
25505                 xy = [x, y];
25506                 this.storeXY(xy);
25507                 supr.setXY.call(this, xy);
25508                 this.sync();
25509             }
25510         }
25511     },
25512
25513     isVisible : function(){
25514         return this.visible;    
25515     },
25516
25517     // private
25518     showAction : function(){
25519         this.visible = true; // track visibility to prevent getStyle calls
25520         if(this.useDisplay === true){
25521             this.setDisplayed("");
25522         }else if(this.lastXY){
25523             supr.setXY.call(this, this.lastXY);
25524         }else if(this.lastLT){
25525             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25526         }
25527     },
25528
25529     // private
25530     hideAction : function(){
25531         this.visible = false;
25532         if(this.useDisplay === true){
25533             this.setDisplayed(false);
25534         }else{
25535             this.setLeftTop(-10000,-10000);
25536         }
25537     },
25538
25539     // overridden Element method
25540     setVisible : function(v, a, d, c, e){
25541         if(v){
25542             this.showAction();
25543         }
25544         if(a && v){
25545             var cb = function(){
25546                 this.sync(true);
25547                 if(c){
25548                     c();
25549                 }
25550             }.createDelegate(this);
25551             supr.setVisible.call(this, true, true, d, cb, e);
25552         }else{
25553             if(!v){
25554                 this.hideUnders(true);
25555             }
25556             var cb = c;
25557             if(a){
25558                 cb = function(){
25559                     this.hideAction();
25560                     if(c){
25561                         c();
25562                     }
25563                 }.createDelegate(this);
25564             }
25565             supr.setVisible.call(this, v, a, d, cb, e);
25566             if(v){
25567                 this.sync(true);
25568             }else if(!a){
25569                 this.hideAction();
25570             }
25571         }
25572     },
25573
25574     storeXY : function(xy){
25575         delete this.lastLT;
25576         this.lastXY = xy;
25577     },
25578
25579     storeLeftTop : function(left, top){
25580         delete this.lastXY;
25581         this.lastLT = [left, top];
25582     },
25583
25584     // private
25585     beforeFx : function(){
25586         this.beforeAction();
25587         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25588     },
25589
25590     // private
25591     afterFx : function(){
25592         Roo.Layer.superclass.afterFx.apply(this, arguments);
25593         this.sync(this.isVisible());
25594     },
25595
25596     // private
25597     beforeAction : function(){
25598         if(!this.updating && this.shadow){
25599             this.shadow.hide();
25600         }
25601     },
25602
25603     // overridden Element method
25604     setLeft : function(left){
25605         this.storeLeftTop(left, this.getTop(true));
25606         supr.setLeft.apply(this, arguments);
25607         this.sync();
25608     },
25609
25610     setTop : function(top){
25611         this.storeLeftTop(this.getLeft(true), top);
25612         supr.setTop.apply(this, arguments);
25613         this.sync();
25614     },
25615
25616     setLeftTop : function(left, top){
25617         this.storeLeftTop(left, top);
25618         supr.setLeftTop.apply(this, arguments);
25619         this.sync();
25620     },
25621
25622     setXY : function(xy, a, d, c, e){
25623         this.fixDisplay();
25624         this.beforeAction();
25625         this.storeXY(xy);
25626         var cb = this.createCB(c);
25627         supr.setXY.call(this, xy, a, d, cb, e);
25628         if(!a){
25629             cb();
25630         }
25631     },
25632
25633     // private
25634     createCB : function(c){
25635         var el = this;
25636         return function(){
25637             el.constrainXY();
25638             el.sync(true);
25639             if(c){
25640                 c();
25641             }
25642         };
25643     },
25644
25645     // overridden Element method
25646     setX : function(x, a, d, c, e){
25647         this.setXY([x, this.getY()], a, d, c, e);
25648     },
25649
25650     // overridden Element method
25651     setY : function(y, a, d, c, e){
25652         this.setXY([this.getX(), y], a, d, c, e);
25653     },
25654
25655     // overridden Element method
25656     setSize : function(w, h, a, d, c, e){
25657         this.beforeAction();
25658         var cb = this.createCB(c);
25659         supr.setSize.call(this, w, h, a, d, cb, e);
25660         if(!a){
25661             cb();
25662         }
25663     },
25664
25665     // overridden Element method
25666     setWidth : function(w, a, d, c, e){
25667         this.beforeAction();
25668         var cb = this.createCB(c);
25669         supr.setWidth.call(this, w, a, d, cb, e);
25670         if(!a){
25671             cb();
25672         }
25673     },
25674
25675     // overridden Element method
25676     setHeight : function(h, a, d, c, e){
25677         this.beforeAction();
25678         var cb = this.createCB(c);
25679         supr.setHeight.call(this, h, a, d, cb, e);
25680         if(!a){
25681             cb();
25682         }
25683     },
25684
25685     // overridden Element method
25686     setBounds : function(x, y, w, h, a, d, c, e){
25687         this.beforeAction();
25688         var cb = this.createCB(c);
25689         if(!a){
25690             this.storeXY([x, y]);
25691             supr.setXY.call(this, [x, y]);
25692             supr.setSize.call(this, w, h, a, d, cb, e);
25693             cb();
25694         }else{
25695             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25696         }
25697         return this;
25698     },
25699     
25700     /**
25701      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25702      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25703      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25704      * @param {Number} zindex The new z-index to set
25705      * @return {this} The Layer
25706      */
25707     setZIndex : function(zindex){
25708         this.zindex = zindex;
25709         this.setStyle("z-index", zindex + 2);
25710         if(this.shadow){
25711             this.shadow.setZIndex(zindex + 1);
25712         }
25713         if(this.shim){
25714             this.shim.setStyle("z-index", zindex);
25715         }
25716     }
25717 });
25718 })();/*
25719  * Based on:
25720  * Ext JS Library 1.1.1
25721  * Copyright(c) 2006-2007, Ext JS, LLC.
25722  *
25723  * Originally Released Under LGPL - original licence link has changed is not relivant.
25724  *
25725  * Fork - LGPL
25726  * <script type="text/javascript">
25727  */
25728
25729
25730 /**
25731  * @class Roo.Shadow
25732  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25733  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25734  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25735  * @constructor
25736  * Create a new Shadow
25737  * @param {Object} config The config object
25738  */
25739 Roo.Shadow = function(config){
25740     Roo.apply(this, config);
25741     if(typeof this.mode != "string"){
25742         this.mode = this.defaultMode;
25743     }
25744     var o = this.offset, a = {h: 0};
25745     var rad = Math.floor(this.offset/2);
25746     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25747         case "drop":
25748             a.w = 0;
25749             a.l = a.t = o;
25750             a.t -= 1;
25751             if(Roo.isIE){
25752                 a.l -= this.offset + rad;
25753                 a.t -= this.offset + rad;
25754                 a.w -= rad;
25755                 a.h -= rad;
25756                 a.t += 1;
25757             }
25758         break;
25759         case "sides":
25760             a.w = (o*2);
25761             a.l = -o;
25762             a.t = o-1;
25763             if(Roo.isIE){
25764                 a.l -= (this.offset - rad);
25765                 a.t -= this.offset + rad;
25766                 a.l += 1;
25767                 a.w -= (this.offset - rad)*2;
25768                 a.w -= rad + 1;
25769                 a.h -= 1;
25770             }
25771         break;
25772         case "frame":
25773             a.w = a.h = (o*2);
25774             a.l = a.t = -o;
25775             a.t += 1;
25776             a.h -= 2;
25777             if(Roo.isIE){
25778                 a.l -= (this.offset - rad);
25779                 a.t -= (this.offset - rad);
25780                 a.l += 1;
25781                 a.w -= (this.offset + rad + 1);
25782                 a.h -= (this.offset + rad);
25783                 a.h += 1;
25784             }
25785         break;
25786     };
25787
25788     this.adjusts = a;
25789 };
25790
25791 Roo.Shadow.prototype = {
25792     /**
25793      * @cfg {String} mode
25794      * The shadow display mode.  Supports the following options:<br />
25795      * sides: Shadow displays on both sides and bottom only<br />
25796      * frame: Shadow displays equally on all four sides<br />
25797      * drop: Traditional bottom-right drop shadow (default)
25798      */
25799     /**
25800      * @cfg {String} offset
25801      * The number of pixels to offset the shadow from the element (defaults to 4)
25802      */
25803     offset: 4,
25804
25805     // private
25806     defaultMode: "drop",
25807
25808     /**
25809      * Displays the shadow under the target element
25810      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25811      */
25812     show : function(target){
25813         target = Roo.get(target);
25814         if(!this.el){
25815             this.el = Roo.Shadow.Pool.pull();
25816             if(this.el.dom.nextSibling != target.dom){
25817                 this.el.insertBefore(target);
25818             }
25819         }
25820         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25821         if(Roo.isIE){
25822             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25823         }
25824         this.realign(
25825             target.getLeft(true),
25826             target.getTop(true),
25827             target.getWidth(),
25828             target.getHeight()
25829         );
25830         this.el.dom.style.display = "block";
25831     },
25832
25833     /**
25834      * Returns true if the shadow is visible, else false
25835      */
25836     isVisible : function(){
25837         return this.el ? true : false;  
25838     },
25839
25840     /**
25841      * Direct alignment when values are already available. Show must be called at least once before
25842      * calling this method to ensure it is initialized.
25843      * @param {Number} left The target element left position
25844      * @param {Number} top The target element top position
25845      * @param {Number} width The target element width
25846      * @param {Number} height The target element height
25847      */
25848     realign : function(l, t, w, h){
25849         if(!this.el){
25850             return;
25851         }
25852         var a = this.adjusts, d = this.el.dom, s = d.style;
25853         var iea = 0;
25854         s.left = (l+a.l)+"px";
25855         s.top = (t+a.t)+"px";
25856         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25857  
25858         if(s.width != sws || s.height != shs){
25859             s.width = sws;
25860             s.height = shs;
25861             if(!Roo.isIE){
25862                 var cn = d.childNodes;
25863                 var sww = Math.max(0, (sw-12))+"px";
25864                 cn[0].childNodes[1].style.width = sww;
25865                 cn[1].childNodes[1].style.width = sww;
25866                 cn[2].childNodes[1].style.width = sww;
25867                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25868             }
25869         }
25870     },
25871
25872     /**
25873      * Hides this shadow
25874      */
25875     hide : function(){
25876         if(this.el){
25877             this.el.dom.style.display = "none";
25878             Roo.Shadow.Pool.push(this.el);
25879             delete this.el;
25880         }
25881     },
25882
25883     /**
25884      * Adjust the z-index of this shadow
25885      * @param {Number} zindex The new z-index
25886      */
25887     setZIndex : function(z){
25888         this.zIndex = z;
25889         if(this.el){
25890             this.el.setStyle("z-index", z);
25891         }
25892     }
25893 };
25894
25895 // Private utility class that manages the internal Shadow cache
25896 Roo.Shadow.Pool = function(){
25897     var p = [];
25898     var markup = Roo.isIE ?
25899                  '<div class="x-ie-shadow"></div>' :
25900                  '<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>';
25901     return {
25902         pull : function(){
25903             var sh = p.shift();
25904             if(!sh){
25905                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
25906                 sh.autoBoxAdjust = false;
25907             }
25908             return sh;
25909         },
25910
25911         push : function(sh){
25912             p.push(sh);
25913         }
25914     };
25915 }();/*
25916  * Based on:
25917  * Ext JS Library 1.1.1
25918  * Copyright(c) 2006-2007, Ext JS, LLC.
25919  *
25920  * Originally Released Under LGPL - original licence link has changed is not relivant.
25921  *
25922  * Fork - LGPL
25923  * <script type="text/javascript">
25924  */
25925
25926
25927 /**
25928  * @class Roo.SplitBar
25929  * @extends Roo.util.Observable
25930  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
25931  * <br><br>
25932  * Usage:
25933  * <pre><code>
25934 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
25935                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
25936 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
25937 split.minSize = 100;
25938 split.maxSize = 600;
25939 split.animate = true;
25940 split.on('moved', splitterMoved);
25941 </code></pre>
25942  * @constructor
25943  * Create a new SplitBar
25944  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
25945  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
25946  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25947  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
25948                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
25949                         position of the SplitBar).
25950  */
25951 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
25952     
25953     /** @private */
25954     this.el = Roo.get(dragElement, true);
25955     this.el.dom.unselectable = "on";
25956     /** @private */
25957     this.resizingEl = Roo.get(resizingElement, true);
25958
25959     /**
25960      * @private
25961      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25962      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
25963      * @type Number
25964      */
25965     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
25966     
25967     /**
25968      * The minimum size of the resizing element. (Defaults to 0)
25969      * @type Number
25970      */
25971     this.minSize = 0;
25972     
25973     /**
25974      * The maximum size of the resizing element. (Defaults to 2000)
25975      * @type Number
25976      */
25977     this.maxSize = 2000;
25978     
25979     /**
25980      * Whether to animate the transition to the new size
25981      * @type Boolean
25982      */
25983     this.animate = false;
25984     
25985     /**
25986      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
25987      * @type Boolean
25988      */
25989     this.useShim = false;
25990     
25991     /** @private */
25992     this.shim = null;
25993     
25994     if(!existingProxy){
25995         /** @private */
25996         this.proxy = Roo.SplitBar.createProxy(this.orientation);
25997     }else{
25998         this.proxy = Roo.get(existingProxy).dom;
25999     }
26000     /** @private */
26001     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26002     
26003     /** @private */
26004     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26005     
26006     /** @private */
26007     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26008     
26009     /** @private */
26010     this.dragSpecs = {};
26011     
26012     /**
26013      * @private The adapter to use to positon and resize elements
26014      */
26015     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26016     this.adapter.init(this);
26017     
26018     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26019         /** @private */
26020         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26021         this.el.addClass("x-splitbar-h");
26022     }else{
26023         /** @private */
26024         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26025         this.el.addClass("x-splitbar-v");
26026     }
26027     
26028     this.addEvents({
26029         /**
26030          * @event resize
26031          * Fires when the splitter is moved (alias for {@link #event-moved})
26032          * @param {Roo.SplitBar} this
26033          * @param {Number} newSize the new width or height
26034          */
26035         "resize" : true,
26036         /**
26037          * @event moved
26038          * Fires when the splitter is moved
26039          * @param {Roo.SplitBar} this
26040          * @param {Number} newSize the new width or height
26041          */
26042         "moved" : true,
26043         /**
26044          * @event beforeresize
26045          * Fires before the splitter is dragged
26046          * @param {Roo.SplitBar} this
26047          */
26048         "beforeresize" : true,
26049
26050         "beforeapply" : true
26051     });
26052
26053     Roo.util.Observable.call(this);
26054 };
26055
26056 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26057     onStartProxyDrag : function(x, y){
26058         this.fireEvent("beforeresize", this);
26059         if(!this.overlay){
26060             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26061             o.unselectable();
26062             o.enableDisplayMode("block");
26063             // all splitbars share the same overlay
26064             Roo.SplitBar.prototype.overlay = o;
26065         }
26066         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26067         this.overlay.show();
26068         Roo.get(this.proxy).setDisplayed("block");
26069         var size = this.adapter.getElementSize(this);
26070         this.activeMinSize = this.getMinimumSize();;
26071         this.activeMaxSize = this.getMaximumSize();;
26072         var c1 = size - this.activeMinSize;
26073         var c2 = Math.max(this.activeMaxSize - size, 0);
26074         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26075             this.dd.resetConstraints();
26076             this.dd.setXConstraint(
26077                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26078                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26079             );
26080             this.dd.setYConstraint(0, 0);
26081         }else{
26082             this.dd.resetConstraints();
26083             this.dd.setXConstraint(0, 0);
26084             this.dd.setYConstraint(
26085                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26086                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26087             );
26088          }
26089         this.dragSpecs.startSize = size;
26090         this.dragSpecs.startPoint = [x, y];
26091         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26092     },
26093     
26094     /** 
26095      * @private Called after the drag operation by the DDProxy
26096      */
26097     onEndProxyDrag : function(e){
26098         Roo.get(this.proxy).setDisplayed(false);
26099         var endPoint = Roo.lib.Event.getXY(e);
26100         if(this.overlay){
26101             this.overlay.hide();
26102         }
26103         var newSize;
26104         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26105             newSize = this.dragSpecs.startSize + 
26106                 (this.placement == Roo.SplitBar.LEFT ?
26107                     endPoint[0] - this.dragSpecs.startPoint[0] :
26108                     this.dragSpecs.startPoint[0] - endPoint[0]
26109                 );
26110         }else{
26111             newSize = this.dragSpecs.startSize + 
26112                 (this.placement == Roo.SplitBar.TOP ?
26113                     endPoint[1] - this.dragSpecs.startPoint[1] :
26114                     this.dragSpecs.startPoint[1] - endPoint[1]
26115                 );
26116         }
26117         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26118         if(newSize != this.dragSpecs.startSize){
26119             if(this.fireEvent('beforeapply', this, newSize) !== false){
26120                 this.adapter.setElementSize(this, newSize);
26121                 this.fireEvent("moved", this, newSize);
26122                 this.fireEvent("resize", this, newSize);
26123             }
26124         }
26125     },
26126     
26127     /**
26128      * Get the adapter this SplitBar uses
26129      * @return The adapter object
26130      */
26131     getAdapter : function(){
26132         return this.adapter;
26133     },
26134     
26135     /**
26136      * Set the adapter this SplitBar uses
26137      * @param {Object} adapter A SplitBar adapter object
26138      */
26139     setAdapter : function(adapter){
26140         this.adapter = adapter;
26141         this.adapter.init(this);
26142     },
26143     
26144     /**
26145      * Gets the minimum size for the resizing element
26146      * @return {Number} The minimum size
26147      */
26148     getMinimumSize : function(){
26149         return this.minSize;
26150     },
26151     
26152     /**
26153      * Sets the minimum size for the resizing element
26154      * @param {Number} minSize The minimum size
26155      */
26156     setMinimumSize : function(minSize){
26157         this.minSize = minSize;
26158     },
26159     
26160     /**
26161      * Gets the maximum size for the resizing element
26162      * @return {Number} The maximum size
26163      */
26164     getMaximumSize : function(){
26165         return this.maxSize;
26166     },
26167     
26168     /**
26169      * Sets the maximum size for the resizing element
26170      * @param {Number} maxSize The maximum size
26171      */
26172     setMaximumSize : function(maxSize){
26173         this.maxSize = maxSize;
26174     },
26175     
26176     /**
26177      * Sets the initialize size for the resizing element
26178      * @param {Number} size The initial size
26179      */
26180     setCurrentSize : function(size){
26181         var oldAnimate = this.animate;
26182         this.animate = false;
26183         this.adapter.setElementSize(this, size);
26184         this.animate = oldAnimate;
26185     },
26186     
26187     /**
26188      * Destroy this splitbar. 
26189      * @param {Boolean} removeEl True to remove the element
26190      */
26191     destroy : function(removeEl){
26192         if(this.shim){
26193             this.shim.remove();
26194         }
26195         this.dd.unreg();
26196         this.proxy.parentNode.removeChild(this.proxy);
26197         if(removeEl){
26198             this.el.remove();
26199         }
26200     }
26201 });
26202
26203 /**
26204  * @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.
26205  */
26206 Roo.SplitBar.createProxy = function(dir){
26207     var proxy = new Roo.Element(document.createElement("div"));
26208     proxy.unselectable();
26209     var cls = 'x-splitbar-proxy';
26210     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26211     document.body.appendChild(proxy.dom);
26212     return proxy.dom;
26213 };
26214
26215 /** 
26216  * @class Roo.SplitBar.BasicLayoutAdapter
26217  * Default Adapter. It assumes the splitter and resizing element are not positioned
26218  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26219  */
26220 Roo.SplitBar.BasicLayoutAdapter = function(){
26221 };
26222
26223 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26224     // do nothing for now
26225     init : function(s){
26226     
26227     },
26228     /**
26229      * Called before drag operations to get the current size of the resizing element. 
26230      * @param {Roo.SplitBar} s The SplitBar using this adapter
26231      */
26232      getElementSize : function(s){
26233         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26234             return s.resizingEl.getWidth();
26235         }else{
26236             return s.resizingEl.getHeight();
26237         }
26238     },
26239     
26240     /**
26241      * Called after drag operations to set the size of the resizing element.
26242      * @param {Roo.SplitBar} s The SplitBar using this adapter
26243      * @param {Number} newSize The new size to set
26244      * @param {Function} onComplete A function to be invoked when resizing is complete
26245      */
26246     setElementSize : function(s, newSize, onComplete){
26247         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26248             if(!s.animate){
26249                 s.resizingEl.setWidth(newSize);
26250                 if(onComplete){
26251                     onComplete(s, newSize);
26252                 }
26253             }else{
26254                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26255             }
26256         }else{
26257             
26258             if(!s.animate){
26259                 s.resizingEl.setHeight(newSize);
26260                 if(onComplete){
26261                     onComplete(s, newSize);
26262                 }
26263             }else{
26264                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26265             }
26266         }
26267     }
26268 };
26269
26270 /** 
26271  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26272  * @extends Roo.SplitBar.BasicLayoutAdapter
26273  * Adapter that  moves the splitter element to align with the resized sizing element. 
26274  * Used with an absolute positioned SplitBar.
26275  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26276  * document.body, make sure you assign an id to the body element.
26277  */
26278 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26279     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26280     this.container = Roo.get(container);
26281 };
26282
26283 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26284     init : function(s){
26285         this.basic.init(s);
26286     },
26287     
26288     getElementSize : function(s){
26289         return this.basic.getElementSize(s);
26290     },
26291     
26292     setElementSize : function(s, newSize, onComplete){
26293         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26294     },
26295     
26296     moveSplitter : function(s){
26297         var yes = Roo.SplitBar;
26298         switch(s.placement){
26299             case yes.LEFT:
26300                 s.el.setX(s.resizingEl.getRight());
26301                 break;
26302             case yes.RIGHT:
26303                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26304                 break;
26305             case yes.TOP:
26306                 s.el.setY(s.resizingEl.getBottom());
26307                 break;
26308             case yes.BOTTOM:
26309                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26310                 break;
26311         }
26312     }
26313 };
26314
26315 /**
26316  * Orientation constant - Create a vertical SplitBar
26317  * @static
26318  * @type Number
26319  */
26320 Roo.SplitBar.VERTICAL = 1;
26321
26322 /**
26323  * Orientation constant - Create a horizontal SplitBar
26324  * @static
26325  * @type Number
26326  */
26327 Roo.SplitBar.HORIZONTAL = 2;
26328
26329 /**
26330  * Placement constant - The resizing element is to the left of the splitter element
26331  * @static
26332  * @type Number
26333  */
26334 Roo.SplitBar.LEFT = 1;
26335
26336 /**
26337  * Placement constant - The resizing element is to the right of the splitter element
26338  * @static
26339  * @type Number
26340  */
26341 Roo.SplitBar.RIGHT = 2;
26342
26343 /**
26344  * Placement constant - The resizing element is positioned above the splitter element
26345  * @static
26346  * @type Number
26347  */
26348 Roo.SplitBar.TOP = 3;
26349
26350 /**
26351  * Placement constant - The resizing element is positioned under splitter element
26352  * @static
26353  * @type Number
26354  */
26355 Roo.SplitBar.BOTTOM = 4;
26356 /*
26357  * Based on:
26358  * Ext JS Library 1.1.1
26359  * Copyright(c) 2006-2007, Ext JS, LLC.
26360  *
26361  * Originally Released Under LGPL - original licence link has changed is not relivant.
26362  *
26363  * Fork - LGPL
26364  * <script type="text/javascript">
26365  */
26366
26367 /**
26368  * @class Roo.View
26369  * @extends Roo.util.Observable
26370  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26371  * This class also supports single and multi selection modes. <br>
26372  * Create a data model bound view:
26373  <pre><code>
26374  var store = new Roo.data.Store(...);
26375
26376  var view = new Roo.View({
26377     el : "my-element",
26378     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26379  
26380     singleSelect: true,
26381     selectedClass: "ydataview-selected",
26382     store: store
26383  });
26384
26385  // listen for node click?
26386  view.on("click", function(vw, index, node, e){
26387  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26388  });
26389
26390  // load XML data
26391  dataModel.load("foobar.xml");
26392  </code></pre>
26393  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26394  * <br><br>
26395  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26396  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26397  * 
26398  * Note: old style constructor is still suported (container, template, config)
26399  * 
26400  * @constructor
26401  * Create a new View
26402  * @param {Object} config The config object
26403  * 
26404  */
26405 Roo.View = function(config, depreciated_tpl, depreciated_config){
26406     
26407     this.parent = false;
26408     
26409     if (typeof(depreciated_tpl) == 'undefined') {
26410         // new way.. - universal constructor.
26411         Roo.apply(this, config);
26412         this.el  = Roo.get(this.el);
26413     } else {
26414         // old format..
26415         this.el  = Roo.get(config);
26416         this.tpl = depreciated_tpl;
26417         Roo.apply(this, depreciated_config);
26418     }
26419     this.wrapEl  = this.el.wrap().wrap();
26420     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26421     
26422     
26423     if(typeof(this.tpl) == "string"){
26424         this.tpl = new Roo.Template(this.tpl);
26425     } else {
26426         // support xtype ctors..
26427         this.tpl = new Roo.factory(this.tpl, Roo);
26428     }
26429     
26430     
26431     this.tpl.compile();
26432     
26433     /** @private */
26434     this.addEvents({
26435         /**
26436          * @event beforeclick
26437          * Fires before a click is processed. Returns false to cancel the default action.
26438          * @param {Roo.View} this
26439          * @param {Number} index The index of the target node
26440          * @param {HTMLElement} node The target node
26441          * @param {Roo.EventObject} e The raw event object
26442          */
26443             "beforeclick" : true,
26444         /**
26445          * @event click
26446          * Fires when a template node is clicked.
26447          * @param {Roo.View} this
26448          * @param {Number} index The index of the target node
26449          * @param {HTMLElement} node The target node
26450          * @param {Roo.EventObject} e The raw event object
26451          */
26452             "click" : true,
26453         /**
26454          * @event dblclick
26455          * Fires when a template node is double clicked.
26456          * @param {Roo.View} this
26457          * @param {Number} index The index of the target node
26458          * @param {HTMLElement} node The target node
26459          * @param {Roo.EventObject} e The raw event object
26460          */
26461             "dblclick" : true,
26462         /**
26463          * @event contextmenu
26464          * Fires when a template node is right clicked.
26465          * @param {Roo.View} this
26466          * @param {Number} index The index of the target node
26467          * @param {HTMLElement} node The target node
26468          * @param {Roo.EventObject} e The raw event object
26469          */
26470             "contextmenu" : true,
26471         /**
26472          * @event selectionchange
26473          * Fires when the selected nodes change.
26474          * @param {Roo.View} this
26475          * @param {Array} selections Array of the selected nodes
26476          */
26477             "selectionchange" : true,
26478     
26479         /**
26480          * @event beforeselect
26481          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26482          * @param {Roo.View} this
26483          * @param {HTMLElement} node The node to be selected
26484          * @param {Array} selections Array of currently selected nodes
26485          */
26486             "beforeselect" : true,
26487         /**
26488          * @event preparedata
26489          * Fires on every row to render, to allow you to change the data.
26490          * @param {Roo.View} this
26491          * @param {Object} data to be rendered (change this)
26492          */
26493           "preparedata" : true
26494           
26495           
26496         });
26497
26498
26499
26500     this.el.on({
26501         "click": this.onClick,
26502         "dblclick": this.onDblClick,
26503         "contextmenu": this.onContextMenu,
26504         scope:this
26505     });
26506
26507     this.selections = [];
26508     this.nodes = [];
26509     this.cmp = new Roo.CompositeElementLite([]);
26510     if(this.store){
26511         this.store = Roo.factory(this.store, Roo.data);
26512         this.setStore(this.store, true);
26513     }
26514     
26515     if ( this.footer && this.footer.xtype) {
26516            
26517          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26518         
26519         this.footer.dataSource = this.store;
26520         this.footer.container = fctr;
26521         this.footer = Roo.factory(this.footer, Roo);
26522         fctr.insertFirst(this.el);
26523         
26524         // this is a bit insane - as the paging toolbar seems to detach the el..
26525 //        dom.parentNode.parentNode.parentNode
26526          // they get detached?
26527     }
26528     
26529     
26530     Roo.View.superclass.constructor.call(this);
26531     
26532     
26533 };
26534
26535 Roo.extend(Roo.View, Roo.util.Observable, {
26536     
26537      /**
26538      * @cfg {Roo.data.Store} store Data store to load data from.
26539      */
26540     store : false,
26541     
26542     /**
26543      * @cfg {String|Roo.Element} el The container element.
26544      */
26545     el : '',
26546     
26547     /**
26548      * @cfg {String|Roo.Template} tpl The template used by this View 
26549      */
26550     tpl : false,
26551     /**
26552      * @cfg {String} dataName the named area of the template to use as the data area
26553      *                          Works with domtemplates roo-name="name"
26554      */
26555     dataName: false,
26556     /**
26557      * @cfg {String} selectedClass The css class to add to selected nodes
26558      */
26559     selectedClass : "x-view-selected",
26560      /**
26561      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26562      */
26563     emptyText : "",
26564     
26565     /**
26566      * @cfg {String} text to display on mask (default Loading)
26567      */
26568     mask : false,
26569     /**
26570      * @cfg {Boolean} multiSelect Allow multiple selection
26571      */
26572     multiSelect : false,
26573     /**
26574      * @cfg {Boolean} singleSelect Allow single selection
26575      */
26576     singleSelect:  false,
26577     
26578     /**
26579      * @cfg {Boolean} toggleSelect - selecting 
26580      */
26581     toggleSelect : false,
26582     
26583     /**
26584      * @cfg {Boolean} tickable - selecting 
26585      */
26586     tickable : false,
26587     
26588     /**
26589      * Returns the element this view is bound to.
26590      * @return {Roo.Element}
26591      */
26592     getEl : function(){
26593         return this.wrapEl;
26594     },
26595     
26596     
26597
26598     /**
26599      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26600      */
26601     refresh : function(){
26602         //Roo.log('refresh');
26603         var t = this.tpl;
26604         
26605         // if we are using something like 'domtemplate', then
26606         // the what gets used is:
26607         // t.applySubtemplate(NAME, data, wrapping data..)
26608         // the outer template then get' applied with
26609         //     the store 'extra data'
26610         // and the body get's added to the
26611         //      roo-name="data" node?
26612         //      <span class='roo-tpl-{name}'></span> ?????
26613         
26614         
26615         
26616         this.clearSelections();
26617         this.el.update("");
26618         var html = [];
26619         var records = this.store.getRange();
26620         if(records.length < 1) {
26621             
26622             // is this valid??  = should it render a template??
26623             
26624             this.el.update(this.emptyText);
26625             return;
26626         }
26627         var el = this.el;
26628         if (this.dataName) {
26629             this.el.update(t.apply(this.store.meta)); //????
26630             el = this.el.child('.roo-tpl-' + this.dataName);
26631         }
26632         
26633         for(var i = 0, len = records.length; i < len; i++){
26634             var data = this.prepareData(records[i].data, i, records[i]);
26635             this.fireEvent("preparedata", this, data, i, records[i]);
26636             
26637             var d = Roo.apply({}, data);
26638             
26639             if(this.tickable){
26640                 Roo.apply(d, {'roo-id' : Roo.id()});
26641                 
26642                 var _this = this;
26643             
26644                 Roo.each(this.parent.item, function(item){
26645                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26646                         return;
26647                     }
26648                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26649                 });
26650             }
26651             
26652             html[html.length] = Roo.util.Format.trim(
26653                 this.dataName ?
26654                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26655                     t.apply(d)
26656             );
26657         }
26658         
26659         
26660         
26661         el.update(html.join(""));
26662         this.nodes = el.dom.childNodes;
26663         this.updateIndexes(0);
26664     },
26665     
26666
26667     /**
26668      * Function to override to reformat the data that is sent to
26669      * the template for each node.
26670      * DEPRICATED - use the preparedata event handler.
26671      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26672      * a JSON object for an UpdateManager bound view).
26673      */
26674     prepareData : function(data, index, record)
26675     {
26676         this.fireEvent("preparedata", this, data, index, record);
26677         return data;
26678     },
26679
26680     onUpdate : function(ds, record){
26681         // Roo.log('on update');   
26682         this.clearSelections();
26683         var index = this.store.indexOf(record);
26684         var n = this.nodes[index];
26685         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26686         n.parentNode.removeChild(n);
26687         this.updateIndexes(index, index);
26688     },
26689
26690     
26691     
26692 // --------- FIXME     
26693     onAdd : function(ds, records, index)
26694     {
26695         //Roo.log(['on Add', ds, records, index] );        
26696         this.clearSelections();
26697         if(this.nodes.length == 0){
26698             this.refresh();
26699             return;
26700         }
26701         var n = this.nodes[index];
26702         for(var i = 0, len = records.length; i < len; i++){
26703             var d = this.prepareData(records[i].data, i, records[i]);
26704             if(n){
26705                 this.tpl.insertBefore(n, d);
26706             }else{
26707                 
26708                 this.tpl.append(this.el, d);
26709             }
26710         }
26711         this.updateIndexes(index);
26712     },
26713
26714     onRemove : function(ds, record, index){
26715        // Roo.log('onRemove');
26716         this.clearSelections();
26717         var el = this.dataName  ?
26718             this.el.child('.roo-tpl-' + this.dataName) :
26719             this.el; 
26720         
26721         el.dom.removeChild(this.nodes[index]);
26722         this.updateIndexes(index);
26723     },
26724
26725     /**
26726      * Refresh an individual node.
26727      * @param {Number} index
26728      */
26729     refreshNode : function(index){
26730         this.onUpdate(this.store, this.store.getAt(index));
26731     },
26732
26733     updateIndexes : function(startIndex, endIndex){
26734         var ns = this.nodes;
26735         startIndex = startIndex || 0;
26736         endIndex = endIndex || ns.length - 1;
26737         for(var i = startIndex; i <= endIndex; i++){
26738             ns[i].nodeIndex = i;
26739         }
26740     },
26741
26742     /**
26743      * Changes the data store this view uses and refresh the view.
26744      * @param {Store} store
26745      */
26746     setStore : function(store, initial){
26747         if(!initial && this.store){
26748             this.store.un("datachanged", this.refresh);
26749             this.store.un("add", this.onAdd);
26750             this.store.un("remove", this.onRemove);
26751             this.store.un("update", this.onUpdate);
26752             this.store.un("clear", this.refresh);
26753             this.store.un("beforeload", this.onBeforeLoad);
26754             this.store.un("load", this.onLoad);
26755             this.store.un("loadexception", this.onLoad);
26756         }
26757         if(store){
26758           
26759             store.on("datachanged", this.refresh, this);
26760             store.on("add", this.onAdd, this);
26761             store.on("remove", this.onRemove, this);
26762             store.on("update", this.onUpdate, this);
26763             store.on("clear", this.refresh, this);
26764             store.on("beforeload", this.onBeforeLoad, this);
26765             store.on("load", this.onLoad, this);
26766             store.on("loadexception", this.onLoad, this);
26767         }
26768         
26769         if(store){
26770             this.refresh();
26771         }
26772     },
26773     /**
26774      * onbeforeLoad - masks the loading area.
26775      *
26776      */
26777     onBeforeLoad : function(store,opts)
26778     {
26779          //Roo.log('onBeforeLoad');   
26780         if (!opts.add) {
26781             this.el.update("");
26782         }
26783         this.el.mask(this.mask ? this.mask : "Loading" ); 
26784     },
26785     onLoad : function ()
26786     {
26787         this.el.unmask();
26788     },
26789     
26790
26791     /**
26792      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26793      * @param {HTMLElement} node
26794      * @return {HTMLElement} The template node
26795      */
26796     findItemFromChild : function(node){
26797         var el = this.dataName  ?
26798             this.el.child('.roo-tpl-' + this.dataName,true) :
26799             this.el.dom; 
26800         
26801         if(!node || node.parentNode == el){
26802                     return node;
26803             }
26804             var p = node.parentNode;
26805             while(p && p != el){
26806             if(p.parentNode == el){
26807                 return p;
26808             }
26809             p = p.parentNode;
26810         }
26811             return null;
26812     },
26813
26814     /** @ignore */
26815     onClick : function(e){
26816         var item = this.findItemFromChild(e.getTarget());
26817         if(item){
26818             var index = this.indexOf(item);
26819             if(this.onItemClick(item, index, e) !== false){
26820                 this.fireEvent("click", this, index, item, e);
26821             }
26822         }else{
26823             this.clearSelections();
26824         }
26825     },
26826
26827     /** @ignore */
26828     onContextMenu : function(e){
26829         var item = this.findItemFromChild(e.getTarget());
26830         if(item){
26831             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26832         }
26833     },
26834
26835     /** @ignore */
26836     onDblClick : function(e){
26837         var item = this.findItemFromChild(e.getTarget());
26838         if(item){
26839             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26840         }
26841     },
26842
26843     onItemClick : function(item, index, e)
26844     {
26845         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26846             return false;
26847         }
26848         if (this.toggleSelect) {
26849             var m = this.isSelected(item) ? 'unselect' : 'select';
26850             //Roo.log(m);
26851             var _t = this;
26852             _t[m](item, true, false);
26853             return true;
26854         }
26855         if(this.multiSelect || this.singleSelect){
26856             if(this.multiSelect && e.shiftKey && this.lastSelection){
26857                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26858             }else{
26859                 this.select(item, this.multiSelect && e.ctrlKey);
26860                 this.lastSelection = item;
26861             }
26862             
26863             if(!this.tickable){
26864                 e.preventDefault();
26865             }
26866             
26867         }
26868         return true;
26869     },
26870
26871     /**
26872      * Get the number of selected nodes.
26873      * @return {Number}
26874      */
26875     getSelectionCount : function(){
26876         return this.selections.length;
26877     },
26878
26879     /**
26880      * Get the currently selected nodes.
26881      * @return {Array} An array of HTMLElements
26882      */
26883     getSelectedNodes : function(){
26884         return this.selections;
26885     },
26886
26887     /**
26888      * Get the indexes of the selected nodes.
26889      * @return {Array}
26890      */
26891     getSelectedIndexes : function(){
26892         var indexes = [], s = this.selections;
26893         for(var i = 0, len = s.length; i < len; i++){
26894             indexes.push(s[i].nodeIndex);
26895         }
26896         return indexes;
26897     },
26898
26899     /**
26900      * Clear all selections
26901      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
26902      */
26903     clearSelections : function(suppressEvent){
26904         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
26905             this.cmp.elements = this.selections;
26906             this.cmp.removeClass(this.selectedClass);
26907             this.selections = [];
26908             if(!suppressEvent){
26909                 this.fireEvent("selectionchange", this, this.selections);
26910             }
26911         }
26912     },
26913
26914     /**
26915      * Returns true if the passed node is selected
26916      * @param {HTMLElement/Number} node The node or node index
26917      * @return {Boolean}
26918      */
26919     isSelected : function(node){
26920         var s = this.selections;
26921         if(s.length < 1){
26922             return false;
26923         }
26924         node = this.getNode(node);
26925         return s.indexOf(node) !== -1;
26926     },
26927
26928     /**
26929      * Selects nodes.
26930      * @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
26931      * @param {Boolean} keepExisting (optional) true to keep existing selections
26932      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26933      */
26934     select : function(nodeInfo, keepExisting, suppressEvent){
26935         if(nodeInfo instanceof Array){
26936             if(!keepExisting){
26937                 this.clearSelections(true);
26938             }
26939             for(var i = 0, len = nodeInfo.length; i < len; i++){
26940                 this.select(nodeInfo[i], true, true);
26941             }
26942             return;
26943         } 
26944         var node = this.getNode(nodeInfo);
26945         if(!node || this.isSelected(node)){
26946             return; // already selected.
26947         }
26948         if(!keepExisting){
26949             this.clearSelections(true);
26950         }
26951         
26952         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
26953             Roo.fly(node).addClass(this.selectedClass);
26954             this.selections.push(node);
26955             if(!suppressEvent){
26956                 this.fireEvent("selectionchange", this, this.selections);
26957             }
26958         }
26959         
26960         
26961     },
26962       /**
26963      * Unselects nodes.
26964      * @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
26965      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
26966      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26967      */
26968     unselect : function(nodeInfo, keepExisting, suppressEvent)
26969     {
26970         if(nodeInfo instanceof Array){
26971             Roo.each(this.selections, function(s) {
26972                 this.unselect(s, nodeInfo);
26973             }, this);
26974             return;
26975         }
26976         var node = this.getNode(nodeInfo);
26977         if(!node || !this.isSelected(node)){
26978             //Roo.log("not selected");
26979             return; // not selected.
26980         }
26981         // fireevent???
26982         var ns = [];
26983         Roo.each(this.selections, function(s) {
26984             if (s == node ) {
26985                 Roo.fly(node).removeClass(this.selectedClass);
26986
26987                 return;
26988             }
26989             ns.push(s);
26990         },this);
26991         
26992         this.selections= ns;
26993         this.fireEvent("selectionchange", this, this.selections);
26994     },
26995
26996     /**
26997      * Gets a template node.
26998      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26999      * @return {HTMLElement} The node or null if it wasn't found
27000      */
27001     getNode : function(nodeInfo){
27002         if(typeof nodeInfo == "string"){
27003             return document.getElementById(nodeInfo);
27004         }else if(typeof nodeInfo == "number"){
27005             return this.nodes[nodeInfo];
27006         }
27007         return nodeInfo;
27008     },
27009
27010     /**
27011      * Gets a range template nodes.
27012      * @param {Number} startIndex
27013      * @param {Number} endIndex
27014      * @return {Array} An array of nodes
27015      */
27016     getNodes : function(start, end){
27017         var ns = this.nodes;
27018         start = start || 0;
27019         end = typeof end == "undefined" ? ns.length - 1 : end;
27020         var nodes = [];
27021         if(start <= end){
27022             for(var i = start; i <= end; i++){
27023                 nodes.push(ns[i]);
27024             }
27025         } else{
27026             for(var i = start; i >= end; i--){
27027                 nodes.push(ns[i]);
27028             }
27029         }
27030         return nodes;
27031     },
27032
27033     /**
27034      * Finds the index of the passed node
27035      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27036      * @return {Number} The index of the node or -1
27037      */
27038     indexOf : function(node){
27039         node = this.getNode(node);
27040         if(typeof node.nodeIndex == "number"){
27041             return node.nodeIndex;
27042         }
27043         var ns = this.nodes;
27044         for(var i = 0, len = ns.length; i < len; i++){
27045             if(ns[i] == node){
27046                 return i;
27047             }
27048         }
27049         return -1;
27050     }
27051 });
27052 /*
27053  * Based on:
27054  * Ext JS Library 1.1.1
27055  * Copyright(c) 2006-2007, Ext JS, LLC.
27056  *
27057  * Originally Released Under LGPL - original licence link has changed is not relivant.
27058  *
27059  * Fork - LGPL
27060  * <script type="text/javascript">
27061  */
27062
27063 /**
27064  * @class Roo.JsonView
27065  * @extends Roo.View
27066  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27067 <pre><code>
27068 var view = new Roo.JsonView({
27069     container: "my-element",
27070     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27071     multiSelect: true, 
27072     jsonRoot: "data" 
27073 });
27074
27075 // listen for node click?
27076 view.on("click", function(vw, index, node, e){
27077     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27078 });
27079
27080 // direct load of JSON data
27081 view.load("foobar.php");
27082
27083 // Example from my blog list
27084 var tpl = new Roo.Template(
27085     '&lt;div class="entry"&gt;' +
27086     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27087     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27088     "&lt;/div&gt;&lt;hr /&gt;"
27089 );
27090
27091 var moreView = new Roo.JsonView({
27092     container :  "entry-list", 
27093     template : tpl,
27094     jsonRoot: "posts"
27095 });
27096 moreView.on("beforerender", this.sortEntries, this);
27097 moreView.load({
27098     url: "/blog/get-posts.php",
27099     params: "allposts=true",
27100     text: "Loading Blog Entries..."
27101 });
27102 </code></pre>
27103
27104 * Note: old code is supported with arguments : (container, template, config)
27105
27106
27107  * @constructor
27108  * Create a new JsonView
27109  * 
27110  * @param {Object} config The config object
27111  * 
27112  */
27113 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27114     
27115     
27116     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27117
27118     var um = this.el.getUpdateManager();
27119     um.setRenderer(this);
27120     um.on("update", this.onLoad, this);
27121     um.on("failure", this.onLoadException, this);
27122
27123     /**
27124      * @event beforerender
27125      * Fires before rendering of the downloaded JSON data.
27126      * @param {Roo.JsonView} this
27127      * @param {Object} data The JSON data loaded
27128      */
27129     /**
27130      * @event load
27131      * Fires when data is loaded.
27132      * @param {Roo.JsonView} this
27133      * @param {Object} data The JSON data loaded
27134      * @param {Object} response The raw Connect response object
27135      */
27136     /**
27137      * @event loadexception
27138      * Fires when loading fails.
27139      * @param {Roo.JsonView} this
27140      * @param {Object} response The raw Connect response object
27141      */
27142     this.addEvents({
27143         'beforerender' : true,
27144         'load' : true,
27145         'loadexception' : true
27146     });
27147 };
27148 Roo.extend(Roo.JsonView, Roo.View, {
27149     /**
27150      * @type {String} The root property in the loaded JSON object that contains the data
27151      */
27152     jsonRoot : "",
27153
27154     /**
27155      * Refreshes the view.
27156      */
27157     refresh : function(){
27158         this.clearSelections();
27159         this.el.update("");
27160         var html = [];
27161         var o = this.jsonData;
27162         if(o && o.length > 0){
27163             for(var i = 0, len = o.length; i < len; i++){
27164                 var data = this.prepareData(o[i], i, o);
27165                 html[html.length] = this.tpl.apply(data);
27166             }
27167         }else{
27168             html.push(this.emptyText);
27169         }
27170         this.el.update(html.join(""));
27171         this.nodes = this.el.dom.childNodes;
27172         this.updateIndexes(0);
27173     },
27174
27175     /**
27176      * 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.
27177      * @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:
27178      <pre><code>
27179      view.load({
27180          url: "your-url.php",
27181          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27182          callback: yourFunction,
27183          scope: yourObject, //(optional scope)
27184          discardUrl: false,
27185          nocache: false,
27186          text: "Loading...",
27187          timeout: 30,
27188          scripts: false
27189      });
27190      </code></pre>
27191      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27192      * 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.
27193      * @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}
27194      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27195      * @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.
27196      */
27197     load : function(){
27198         var um = this.el.getUpdateManager();
27199         um.update.apply(um, arguments);
27200     },
27201
27202     // note - render is a standard framework call...
27203     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27204     render : function(el, response){
27205         
27206         this.clearSelections();
27207         this.el.update("");
27208         var o;
27209         try{
27210             if (response != '') {
27211                 o = Roo.util.JSON.decode(response.responseText);
27212                 if(this.jsonRoot){
27213                     
27214                     o = o[this.jsonRoot];
27215                 }
27216             }
27217         } catch(e){
27218         }
27219         /**
27220          * The current JSON data or null
27221          */
27222         this.jsonData = o;
27223         this.beforeRender();
27224         this.refresh();
27225     },
27226
27227 /**
27228  * Get the number of records in the current JSON dataset
27229  * @return {Number}
27230  */
27231     getCount : function(){
27232         return this.jsonData ? this.jsonData.length : 0;
27233     },
27234
27235 /**
27236  * Returns the JSON object for the specified node(s)
27237  * @param {HTMLElement/Array} node The node or an array of nodes
27238  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27239  * you get the JSON object for the node
27240  */
27241     getNodeData : function(node){
27242         if(node instanceof Array){
27243             var data = [];
27244             for(var i = 0, len = node.length; i < len; i++){
27245                 data.push(this.getNodeData(node[i]));
27246             }
27247             return data;
27248         }
27249         return this.jsonData[this.indexOf(node)] || null;
27250     },
27251
27252     beforeRender : function(){
27253         this.snapshot = this.jsonData;
27254         if(this.sortInfo){
27255             this.sort.apply(this, this.sortInfo);
27256         }
27257         this.fireEvent("beforerender", this, this.jsonData);
27258     },
27259
27260     onLoad : function(el, o){
27261         this.fireEvent("load", this, this.jsonData, o);
27262     },
27263
27264     onLoadException : function(el, o){
27265         this.fireEvent("loadexception", this, o);
27266     },
27267
27268 /**
27269  * Filter the data by a specific property.
27270  * @param {String} property A property on your JSON objects
27271  * @param {String/RegExp} value Either string that the property values
27272  * should start with, or a RegExp to test against the property
27273  */
27274     filter : function(property, value){
27275         if(this.jsonData){
27276             var data = [];
27277             var ss = this.snapshot;
27278             if(typeof value == "string"){
27279                 var vlen = value.length;
27280                 if(vlen == 0){
27281                     this.clearFilter();
27282                     return;
27283                 }
27284                 value = value.toLowerCase();
27285                 for(var i = 0, len = ss.length; i < len; i++){
27286                     var o = ss[i];
27287                     if(o[property].substr(0, vlen).toLowerCase() == value){
27288                         data.push(o);
27289                     }
27290                 }
27291             } else if(value.exec){ // regex?
27292                 for(var i = 0, len = ss.length; i < len; i++){
27293                     var o = ss[i];
27294                     if(value.test(o[property])){
27295                         data.push(o);
27296                     }
27297                 }
27298             } else{
27299                 return;
27300             }
27301             this.jsonData = data;
27302             this.refresh();
27303         }
27304     },
27305
27306 /**
27307  * Filter by a function. The passed function will be called with each
27308  * object in the current dataset. If the function returns true the value is kept,
27309  * otherwise it is filtered.
27310  * @param {Function} fn
27311  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27312  */
27313     filterBy : function(fn, scope){
27314         if(this.jsonData){
27315             var data = [];
27316             var ss = this.snapshot;
27317             for(var i = 0, len = ss.length; i < len; i++){
27318                 var o = ss[i];
27319                 if(fn.call(scope || this, o)){
27320                     data.push(o);
27321                 }
27322             }
27323             this.jsonData = data;
27324             this.refresh();
27325         }
27326     },
27327
27328 /**
27329  * Clears the current filter.
27330  */
27331     clearFilter : function(){
27332         if(this.snapshot && this.jsonData != this.snapshot){
27333             this.jsonData = this.snapshot;
27334             this.refresh();
27335         }
27336     },
27337
27338
27339 /**
27340  * Sorts the data for this view and refreshes it.
27341  * @param {String} property A property on your JSON objects to sort on
27342  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27343  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27344  */
27345     sort : function(property, dir, sortType){
27346         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27347         if(this.jsonData){
27348             var p = property;
27349             var dsc = dir && dir.toLowerCase() == "desc";
27350             var f = function(o1, o2){
27351                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27352                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27353                 ;
27354                 if(v1 < v2){
27355                     return dsc ? +1 : -1;
27356                 } else if(v1 > v2){
27357                     return dsc ? -1 : +1;
27358                 } else{
27359                     return 0;
27360                 }
27361             };
27362             this.jsonData.sort(f);
27363             this.refresh();
27364             if(this.jsonData != this.snapshot){
27365                 this.snapshot.sort(f);
27366             }
27367         }
27368     }
27369 });/*
27370  * Based on:
27371  * Ext JS Library 1.1.1
27372  * Copyright(c) 2006-2007, Ext JS, LLC.
27373  *
27374  * Originally Released Under LGPL - original licence link has changed is not relivant.
27375  *
27376  * Fork - LGPL
27377  * <script type="text/javascript">
27378  */
27379  
27380
27381 /**
27382  * @class Roo.ColorPalette
27383  * @extends Roo.Component
27384  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27385  * Here's an example of typical usage:
27386  * <pre><code>
27387 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27388 cp.render('my-div');
27389
27390 cp.on('select', function(palette, selColor){
27391     // do something with selColor
27392 });
27393 </code></pre>
27394  * @constructor
27395  * Create a new ColorPalette
27396  * @param {Object} config The config object
27397  */
27398 Roo.ColorPalette = function(config){
27399     Roo.ColorPalette.superclass.constructor.call(this, config);
27400     this.addEvents({
27401         /**
27402              * @event select
27403              * Fires when a color is selected
27404              * @param {ColorPalette} this
27405              * @param {String} color The 6-digit color hex code (without the # symbol)
27406              */
27407         select: true
27408     });
27409
27410     if(this.handler){
27411         this.on("select", this.handler, this.scope, true);
27412     }
27413 };
27414 Roo.extend(Roo.ColorPalette, Roo.Component, {
27415     /**
27416      * @cfg {String} itemCls
27417      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27418      */
27419     itemCls : "x-color-palette",
27420     /**
27421      * @cfg {String} value
27422      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27423      * the hex codes are case-sensitive.
27424      */
27425     value : null,
27426     clickEvent:'click',
27427     // private
27428     ctype: "Roo.ColorPalette",
27429
27430     /**
27431      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27432      */
27433     allowReselect : false,
27434
27435     /**
27436      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27437      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27438      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27439      * of colors with the width setting until the box is symmetrical.</p>
27440      * <p>You can override individual colors if needed:</p>
27441      * <pre><code>
27442 var cp = new Roo.ColorPalette();
27443 cp.colors[0] = "FF0000";  // change the first box to red
27444 </code></pre>
27445
27446 Or you can provide a custom array of your own for complete control:
27447 <pre><code>
27448 var cp = new Roo.ColorPalette();
27449 cp.colors = ["000000", "993300", "333300"];
27450 </code></pre>
27451      * @type Array
27452      */
27453     colors : [
27454         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27455         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27456         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27457         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27458         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27459     ],
27460
27461     // private
27462     onRender : function(container, position){
27463         var t = new Roo.MasterTemplate(
27464             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27465         );
27466         var c = this.colors;
27467         for(var i = 0, len = c.length; i < len; i++){
27468             t.add([c[i]]);
27469         }
27470         var el = document.createElement("div");
27471         el.className = this.itemCls;
27472         t.overwrite(el);
27473         container.dom.insertBefore(el, position);
27474         this.el = Roo.get(el);
27475         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27476         if(this.clickEvent != 'click'){
27477             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27478         }
27479     },
27480
27481     // private
27482     afterRender : function(){
27483         Roo.ColorPalette.superclass.afterRender.call(this);
27484         if(this.value){
27485             var s = this.value;
27486             this.value = null;
27487             this.select(s);
27488         }
27489     },
27490
27491     // private
27492     handleClick : function(e, t){
27493         e.preventDefault();
27494         if(!this.disabled){
27495             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27496             this.select(c.toUpperCase());
27497         }
27498     },
27499
27500     /**
27501      * Selects the specified color in the palette (fires the select event)
27502      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27503      */
27504     select : function(color){
27505         color = color.replace("#", "");
27506         if(color != this.value || this.allowReselect){
27507             var el = this.el;
27508             if(this.value){
27509                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27510             }
27511             el.child("a.color-"+color).addClass("x-color-palette-sel");
27512             this.value = color;
27513             this.fireEvent("select", this, color);
27514         }
27515     }
27516 });/*
27517  * Based on:
27518  * Ext JS Library 1.1.1
27519  * Copyright(c) 2006-2007, Ext JS, LLC.
27520  *
27521  * Originally Released Under LGPL - original licence link has changed is not relivant.
27522  *
27523  * Fork - LGPL
27524  * <script type="text/javascript">
27525  */
27526  
27527 /**
27528  * @class Roo.DatePicker
27529  * @extends Roo.Component
27530  * Simple date picker class.
27531  * @constructor
27532  * Create a new DatePicker
27533  * @param {Object} config The config object
27534  */
27535 Roo.DatePicker = function(config){
27536     Roo.DatePicker.superclass.constructor.call(this, config);
27537
27538     this.value = config && config.value ?
27539                  config.value.clearTime() : new Date().clearTime();
27540
27541     this.addEvents({
27542         /**
27543              * @event select
27544              * Fires when a date is selected
27545              * @param {DatePicker} this
27546              * @param {Date} date The selected date
27547              */
27548         'select': true,
27549         /**
27550              * @event monthchange
27551              * Fires when the displayed month changes 
27552              * @param {DatePicker} this
27553              * @param {Date} date The selected month
27554              */
27555         'monthchange': true
27556     });
27557
27558     if(this.handler){
27559         this.on("select", this.handler,  this.scope || this);
27560     }
27561     // build the disabledDatesRE
27562     if(!this.disabledDatesRE && this.disabledDates){
27563         var dd = this.disabledDates;
27564         var re = "(?:";
27565         for(var i = 0; i < dd.length; i++){
27566             re += dd[i];
27567             if(i != dd.length-1) {
27568                 re += "|";
27569             }
27570         }
27571         this.disabledDatesRE = new RegExp(re + ")");
27572     }
27573 };
27574
27575 Roo.extend(Roo.DatePicker, Roo.Component, {
27576     /**
27577      * @cfg {String} todayText
27578      * The text to display on the button that selects the current date (defaults to "Today")
27579      */
27580     todayText : "Today",
27581     /**
27582      * @cfg {String} okText
27583      * The text to display on the ok button
27584      */
27585     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27586     /**
27587      * @cfg {String} cancelText
27588      * The text to display on the cancel button
27589      */
27590     cancelText : "Cancel",
27591     /**
27592      * @cfg {String} todayTip
27593      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27594      */
27595     todayTip : "{0} (Spacebar)",
27596     /**
27597      * @cfg {Date} minDate
27598      * Minimum allowable date (JavaScript date object, defaults to null)
27599      */
27600     minDate : null,
27601     /**
27602      * @cfg {Date} maxDate
27603      * Maximum allowable date (JavaScript date object, defaults to null)
27604      */
27605     maxDate : null,
27606     /**
27607      * @cfg {String} minText
27608      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27609      */
27610     minText : "This date is before the minimum date",
27611     /**
27612      * @cfg {String} maxText
27613      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27614      */
27615     maxText : "This date is after the maximum date",
27616     /**
27617      * @cfg {String} format
27618      * The default date format string which can be overriden for localization support.  The format must be
27619      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27620      */
27621     format : "m/d/y",
27622     /**
27623      * @cfg {Array} disabledDays
27624      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27625      */
27626     disabledDays : null,
27627     /**
27628      * @cfg {String} disabledDaysText
27629      * The tooltip to display when the date falls on a disabled day (defaults to "")
27630      */
27631     disabledDaysText : "",
27632     /**
27633      * @cfg {RegExp} disabledDatesRE
27634      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27635      */
27636     disabledDatesRE : null,
27637     /**
27638      * @cfg {String} disabledDatesText
27639      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27640      */
27641     disabledDatesText : "",
27642     /**
27643      * @cfg {Boolean} constrainToViewport
27644      * True to constrain the date picker to the viewport (defaults to true)
27645      */
27646     constrainToViewport : true,
27647     /**
27648      * @cfg {Array} monthNames
27649      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27650      */
27651     monthNames : Date.monthNames,
27652     /**
27653      * @cfg {Array} dayNames
27654      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27655      */
27656     dayNames : Date.dayNames,
27657     /**
27658      * @cfg {String} nextText
27659      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27660      */
27661     nextText: 'Next Month (Control+Right)',
27662     /**
27663      * @cfg {String} prevText
27664      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27665      */
27666     prevText: 'Previous Month (Control+Left)',
27667     /**
27668      * @cfg {String} monthYearText
27669      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27670      */
27671     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27672     /**
27673      * @cfg {Number} startDay
27674      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27675      */
27676     startDay : 0,
27677     /**
27678      * @cfg {Bool} showClear
27679      * Show a clear button (usefull for date form elements that can be blank.)
27680      */
27681     
27682     showClear: false,
27683     
27684     /**
27685      * Sets the value of the date field
27686      * @param {Date} value The date to set
27687      */
27688     setValue : function(value){
27689         var old = this.value;
27690         
27691         if (typeof(value) == 'string') {
27692          
27693             value = Date.parseDate(value, this.format);
27694         }
27695         if (!value) {
27696             value = new Date();
27697         }
27698         
27699         this.value = value.clearTime(true);
27700         if(this.el){
27701             this.update(this.value);
27702         }
27703     },
27704
27705     /**
27706      * Gets the current selected value of the date field
27707      * @return {Date} The selected date
27708      */
27709     getValue : function(){
27710         return this.value;
27711     },
27712
27713     // private
27714     focus : function(){
27715         if(this.el){
27716             this.update(this.activeDate);
27717         }
27718     },
27719
27720     // privateval
27721     onRender : function(container, position){
27722         
27723         var m = [
27724              '<table cellspacing="0">',
27725                 '<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>',
27726                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27727         var dn = this.dayNames;
27728         for(var i = 0; i < 7; i++){
27729             var d = this.startDay+i;
27730             if(d > 6){
27731                 d = d-7;
27732             }
27733             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27734         }
27735         m[m.length] = "</tr></thead><tbody><tr>";
27736         for(var i = 0; i < 42; i++) {
27737             if(i % 7 == 0 && i != 0){
27738                 m[m.length] = "</tr><tr>";
27739             }
27740             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27741         }
27742         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27743             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27744
27745         var el = document.createElement("div");
27746         el.className = "x-date-picker";
27747         el.innerHTML = m.join("");
27748
27749         container.dom.insertBefore(el, position);
27750
27751         this.el = Roo.get(el);
27752         this.eventEl = Roo.get(el.firstChild);
27753
27754         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27755             handler: this.showPrevMonth,
27756             scope: this,
27757             preventDefault:true,
27758             stopDefault:true
27759         });
27760
27761         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27762             handler: this.showNextMonth,
27763             scope: this,
27764             preventDefault:true,
27765             stopDefault:true
27766         });
27767
27768         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27769
27770         this.monthPicker = this.el.down('div.x-date-mp');
27771         this.monthPicker.enableDisplayMode('block');
27772         
27773         var kn = new Roo.KeyNav(this.eventEl, {
27774             "left" : function(e){
27775                 e.ctrlKey ?
27776                     this.showPrevMonth() :
27777                     this.update(this.activeDate.add("d", -1));
27778             },
27779
27780             "right" : function(e){
27781                 e.ctrlKey ?
27782                     this.showNextMonth() :
27783                     this.update(this.activeDate.add("d", 1));
27784             },
27785
27786             "up" : function(e){
27787                 e.ctrlKey ?
27788                     this.showNextYear() :
27789                     this.update(this.activeDate.add("d", -7));
27790             },
27791
27792             "down" : function(e){
27793                 e.ctrlKey ?
27794                     this.showPrevYear() :
27795                     this.update(this.activeDate.add("d", 7));
27796             },
27797
27798             "pageUp" : function(e){
27799                 this.showNextMonth();
27800             },
27801
27802             "pageDown" : function(e){
27803                 this.showPrevMonth();
27804             },
27805
27806             "enter" : function(e){
27807                 e.stopPropagation();
27808                 return true;
27809             },
27810
27811             scope : this
27812         });
27813
27814         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27815
27816         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27817
27818         this.el.unselectable();
27819         
27820         this.cells = this.el.select("table.x-date-inner tbody td");
27821         this.textNodes = this.el.query("table.x-date-inner tbody span");
27822
27823         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27824             text: "&#160;",
27825             tooltip: this.monthYearText
27826         });
27827
27828         this.mbtn.on('click', this.showMonthPicker, this);
27829         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27830
27831
27832         var today = (new Date()).dateFormat(this.format);
27833         
27834         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27835         if (this.showClear) {
27836             baseTb.add( new Roo.Toolbar.Fill());
27837         }
27838         baseTb.add({
27839             text: String.format(this.todayText, today),
27840             tooltip: String.format(this.todayTip, today),
27841             handler: this.selectToday,
27842             scope: this
27843         });
27844         
27845         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27846             
27847         //});
27848         if (this.showClear) {
27849             
27850             baseTb.add( new Roo.Toolbar.Fill());
27851             baseTb.add({
27852                 text: '&#160;',
27853                 cls: 'x-btn-icon x-btn-clear',
27854                 handler: function() {
27855                     //this.value = '';
27856                     this.fireEvent("select", this, '');
27857                 },
27858                 scope: this
27859             });
27860         }
27861         
27862         
27863         if(Roo.isIE){
27864             this.el.repaint();
27865         }
27866         this.update(this.value);
27867     },
27868
27869     createMonthPicker : function(){
27870         if(!this.monthPicker.dom.firstChild){
27871             var buf = ['<table border="0" cellspacing="0">'];
27872             for(var i = 0; i < 6; i++){
27873                 buf.push(
27874                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27875                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27876                     i == 0 ?
27877                     '<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>' :
27878                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27879                 );
27880             }
27881             buf.push(
27882                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27883                     this.okText,
27884                     '</button><button type="button" class="x-date-mp-cancel">',
27885                     this.cancelText,
27886                     '</button></td></tr>',
27887                 '</table>'
27888             );
27889             this.monthPicker.update(buf.join(''));
27890             this.monthPicker.on('click', this.onMonthClick, this);
27891             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
27892
27893             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
27894             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
27895
27896             this.mpMonths.each(function(m, a, i){
27897                 i += 1;
27898                 if((i%2) == 0){
27899                     m.dom.xmonth = 5 + Math.round(i * .5);
27900                 }else{
27901                     m.dom.xmonth = Math.round((i-1) * .5);
27902                 }
27903             });
27904         }
27905     },
27906
27907     showMonthPicker : function(){
27908         this.createMonthPicker();
27909         var size = this.el.getSize();
27910         this.monthPicker.setSize(size);
27911         this.monthPicker.child('table').setSize(size);
27912
27913         this.mpSelMonth = (this.activeDate || this.value).getMonth();
27914         this.updateMPMonth(this.mpSelMonth);
27915         this.mpSelYear = (this.activeDate || this.value).getFullYear();
27916         this.updateMPYear(this.mpSelYear);
27917
27918         this.monthPicker.slideIn('t', {duration:.2});
27919     },
27920
27921     updateMPYear : function(y){
27922         this.mpyear = y;
27923         var ys = this.mpYears.elements;
27924         for(var i = 1; i <= 10; i++){
27925             var td = ys[i-1], y2;
27926             if((i%2) == 0){
27927                 y2 = y + Math.round(i * .5);
27928                 td.firstChild.innerHTML = y2;
27929                 td.xyear = y2;
27930             }else{
27931                 y2 = y - (5-Math.round(i * .5));
27932                 td.firstChild.innerHTML = y2;
27933                 td.xyear = y2;
27934             }
27935             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
27936         }
27937     },
27938
27939     updateMPMonth : function(sm){
27940         this.mpMonths.each(function(m, a, i){
27941             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
27942         });
27943     },
27944
27945     selectMPMonth: function(m){
27946         
27947     },
27948
27949     onMonthClick : function(e, t){
27950         e.stopEvent();
27951         var el = new Roo.Element(t), pn;
27952         if(el.is('button.x-date-mp-cancel')){
27953             this.hideMonthPicker();
27954         }
27955         else if(el.is('button.x-date-mp-ok')){
27956             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27957             this.hideMonthPicker();
27958         }
27959         else if(pn = el.up('td.x-date-mp-month', 2)){
27960             this.mpMonths.removeClass('x-date-mp-sel');
27961             pn.addClass('x-date-mp-sel');
27962             this.mpSelMonth = pn.dom.xmonth;
27963         }
27964         else if(pn = el.up('td.x-date-mp-year', 2)){
27965             this.mpYears.removeClass('x-date-mp-sel');
27966             pn.addClass('x-date-mp-sel');
27967             this.mpSelYear = pn.dom.xyear;
27968         }
27969         else if(el.is('a.x-date-mp-prev')){
27970             this.updateMPYear(this.mpyear-10);
27971         }
27972         else if(el.is('a.x-date-mp-next')){
27973             this.updateMPYear(this.mpyear+10);
27974         }
27975     },
27976
27977     onMonthDblClick : function(e, t){
27978         e.stopEvent();
27979         var el = new Roo.Element(t), pn;
27980         if(pn = el.up('td.x-date-mp-month', 2)){
27981             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
27982             this.hideMonthPicker();
27983         }
27984         else if(pn = el.up('td.x-date-mp-year', 2)){
27985             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27986             this.hideMonthPicker();
27987         }
27988     },
27989
27990     hideMonthPicker : function(disableAnim){
27991         if(this.monthPicker){
27992             if(disableAnim === true){
27993                 this.monthPicker.hide();
27994             }else{
27995                 this.monthPicker.slideOut('t', {duration:.2});
27996             }
27997         }
27998     },
27999
28000     // private
28001     showPrevMonth : function(e){
28002         this.update(this.activeDate.add("mo", -1));
28003     },
28004
28005     // private
28006     showNextMonth : function(e){
28007         this.update(this.activeDate.add("mo", 1));
28008     },
28009
28010     // private
28011     showPrevYear : function(){
28012         this.update(this.activeDate.add("y", -1));
28013     },
28014
28015     // private
28016     showNextYear : function(){
28017         this.update(this.activeDate.add("y", 1));
28018     },
28019
28020     // private
28021     handleMouseWheel : function(e){
28022         var delta = e.getWheelDelta();
28023         if(delta > 0){
28024             this.showPrevMonth();
28025             e.stopEvent();
28026         } else if(delta < 0){
28027             this.showNextMonth();
28028             e.stopEvent();
28029         }
28030     },
28031
28032     // private
28033     handleDateClick : function(e, t){
28034         e.stopEvent();
28035         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28036             this.setValue(new Date(t.dateValue));
28037             this.fireEvent("select", this, this.value);
28038         }
28039     },
28040
28041     // private
28042     selectToday : function(){
28043         this.setValue(new Date().clearTime());
28044         this.fireEvent("select", this, this.value);
28045     },
28046
28047     // private
28048     update : function(date)
28049     {
28050         var vd = this.activeDate;
28051         this.activeDate = date;
28052         if(vd && this.el){
28053             var t = date.getTime();
28054             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28055                 this.cells.removeClass("x-date-selected");
28056                 this.cells.each(function(c){
28057                    if(c.dom.firstChild.dateValue == t){
28058                        c.addClass("x-date-selected");
28059                        setTimeout(function(){
28060                             try{c.dom.firstChild.focus();}catch(e){}
28061                        }, 50);
28062                        return false;
28063                    }
28064                 });
28065                 return;
28066             }
28067         }
28068         
28069         var days = date.getDaysInMonth();
28070         var firstOfMonth = date.getFirstDateOfMonth();
28071         var startingPos = firstOfMonth.getDay()-this.startDay;
28072
28073         if(startingPos <= this.startDay){
28074             startingPos += 7;
28075         }
28076
28077         var pm = date.add("mo", -1);
28078         var prevStart = pm.getDaysInMonth()-startingPos;
28079
28080         var cells = this.cells.elements;
28081         var textEls = this.textNodes;
28082         days += startingPos;
28083
28084         // convert everything to numbers so it's fast
28085         var day = 86400000;
28086         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28087         var today = new Date().clearTime().getTime();
28088         var sel = date.clearTime().getTime();
28089         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28090         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28091         var ddMatch = this.disabledDatesRE;
28092         var ddText = this.disabledDatesText;
28093         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28094         var ddaysText = this.disabledDaysText;
28095         var format = this.format;
28096
28097         var setCellClass = function(cal, cell){
28098             cell.title = "";
28099             var t = d.getTime();
28100             cell.firstChild.dateValue = t;
28101             if(t == today){
28102                 cell.className += " x-date-today";
28103                 cell.title = cal.todayText;
28104             }
28105             if(t == sel){
28106                 cell.className += " x-date-selected";
28107                 setTimeout(function(){
28108                     try{cell.firstChild.focus();}catch(e){}
28109                 }, 50);
28110             }
28111             // disabling
28112             if(t < min) {
28113                 cell.className = " x-date-disabled";
28114                 cell.title = cal.minText;
28115                 return;
28116             }
28117             if(t > max) {
28118                 cell.className = " x-date-disabled";
28119                 cell.title = cal.maxText;
28120                 return;
28121             }
28122             if(ddays){
28123                 if(ddays.indexOf(d.getDay()) != -1){
28124                     cell.title = ddaysText;
28125                     cell.className = " x-date-disabled";
28126                 }
28127             }
28128             if(ddMatch && format){
28129                 var fvalue = d.dateFormat(format);
28130                 if(ddMatch.test(fvalue)){
28131                     cell.title = ddText.replace("%0", fvalue);
28132                     cell.className = " x-date-disabled";
28133                 }
28134             }
28135         };
28136
28137         var i = 0;
28138         for(; i < startingPos; i++) {
28139             textEls[i].innerHTML = (++prevStart);
28140             d.setDate(d.getDate()+1);
28141             cells[i].className = "x-date-prevday";
28142             setCellClass(this, cells[i]);
28143         }
28144         for(; i < days; i++){
28145             intDay = i - startingPos + 1;
28146             textEls[i].innerHTML = (intDay);
28147             d.setDate(d.getDate()+1);
28148             cells[i].className = "x-date-active";
28149             setCellClass(this, cells[i]);
28150         }
28151         var extraDays = 0;
28152         for(; i < 42; i++) {
28153              textEls[i].innerHTML = (++extraDays);
28154              d.setDate(d.getDate()+1);
28155              cells[i].className = "x-date-nextday";
28156              setCellClass(this, cells[i]);
28157         }
28158
28159         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28160         this.fireEvent('monthchange', this, date);
28161         
28162         if(!this.internalRender){
28163             var main = this.el.dom.firstChild;
28164             var w = main.offsetWidth;
28165             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28166             Roo.fly(main).setWidth(w);
28167             this.internalRender = true;
28168             // opera does not respect the auto grow header center column
28169             // then, after it gets a width opera refuses to recalculate
28170             // without a second pass
28171             if(Roo.isOpera && !this.secondPass){
28172                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28173                 this.secondPass = true;
28174                 this.update.defer(10, this, [date]);
28175             }
28176         }
28177         
28178         
28179     }
28180 });        /*
28181  * Based on:
28182  * Ext JS Library 1.1.1
28183  * Copyright(c) 2006-2007, Ext JS, LLC.
28184  *
28185  * Originally Released Under LGPL - original licence link has changed is not relivant.
28186  *
28187  * Fork - LGPL
28188  * <script type="text/javascript">
28189  */
28190 /**
28191  * @class Roo.TabPanel
28192  * @extends Roo.util.Observable
28193  * A lightweight tab container.
28194  * <br><br>
28195  * Usage:
28196  * <pre><code>
28197 // basic tabs 1, built from existing content
28198 var tabs = new Roo.TabPanel("tabs1");
28199 tabs.addTab("script", "View Script");
28200 tabs.addTab("markup", "View Markup");
28201 tabs.activate("script");
28202
28203 // more advanced tabs, built from javascript
28204 var jtabs = new Roo.TabPanel("jtabs");
28205 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28206
28207 // set up the UpdateManager
28208 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28209 var updater = tab2.getUpdateManager();
28210 updater.setDefaultUrl("ajax1.htm");
28211 tab2.on('activate', updater.refresh, updater, true);
28212
28213 // Use setUrl for Ajax loading
28214 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28215 tab3.setUrl("ajax2.htm", null, true);
28216
28217 // Disabled tab
28218 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28219 tab4.disable();
28220
28221 jtabs.activate("jtabs-1");
28222  * </code></pre>
28223  * @constructor
28224  * Create a new TabPanel.
28225  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28226  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28227  */
28228 Roo.TabPanel = function(container, config){
28229     /**
28230     * The container element for this TabPanel.
28231     * @type Roo.Element
28232     */
28233     this.el = Roo.get(container, true);
28234     if(config){
28235         if(typeof config == "boolean"){
28236             this.tabPosition = config ? "bottom" : "top";
28237         }else{
28238             Roo.apply(this, config);
28239         }
28240     }
28241     if(this.tabPosition == "bottom"){
28242         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28243         this.el.addClass("x-tabs-bottom");
28244     }
28245     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28246     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28247     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28248     if(Roo.isIE){
28249         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28250     }
28251     if(this.tabPosition != "bottom"){
28252         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28253          * @type Roo.Element
28254          */
28255         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28256         this.el.addClass("x-tabs-top");
28257     }
28258     this.items = [];
28259
28260     this.bodyEl.setStyle("position", "relative");
28261
28262     this.active = null;
28263     this.activateDelegate = this.activate.createDelegate(this);
28264
28265     this.addEvents({
28266         /**
28267          * @event tabchange
28268          * Fires when the active tab changes
28269          * @param {Roo.TabPanel} this
28270          * @param {Roo.TabPanelItem} activePanel The new active tab
28271          */
28272         "tabchange": true,
28273         /**
28274          * @event beforetabchange
28275          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28276          * @param {Roo.TabPanel} this
28277          * @param {Object} e Set cancel to true on this object to cancel the tab change
28278          * @param {Roo.TabPanelItem} tab The tab being changed to
28279          */
28280         "beforetabchange" : true
28281     });
28282
28283     Roo.EventManager.onWindowResize(this.onResize, this);
28284     this.cpad = this.el.getPadding("lr");
28285     this.hiddenCount = 0;
28286
28287
28288     // toolbar on the tabbar support...
28289     if (this.toolbar) {
28290         var tcfg = this.toolbar;
28291         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28292         this.toolbar = new Roo.Toolbar(tcfg);
28293         if (Roo.isSafari) {
28294             var tbl = tcfg.container.child('table', true);
28295             tbl.setAttribute('width', '100%');
28296         }
28297         
28298     }
28299    
28300
28301
28302     Roo.TabPanel.superclass.constructor.call(this);
28303 };
28304
28305 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28306     /*
28307      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28308      */
28309     tabPosition : "top",
28310     /*
28311      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28312      */
28313     currentTabWidth : 0,
28314     /*
28315      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28316      */
28317     minTabWidth : 40,
28318     /*
28319      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28320      */
28321     maxTabWidth : 250,
28322     /*
28323      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28324      */
28325     preferredTabWidth : 175,
28326     /*
28327      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28328      */
28329     resizeTabs : false,
28330     /*
28331      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28332      */
28333     monitorResize : true,
28334     /*
28335      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28336      */
28337     toolbar : false,
28338
28339     /**
28340      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28341      * @param {String} id The id of the div to use <b>or create</b>
28342      * @param {String} text The text for the tab
28343      * @param {String} content (optional) Content to put in the TabPanelItem body
28344      * @param {Boolean} closable (optional) True to create a close icon on the tab
28345      * @return {Roo.TabPanelItem} The created TabPanelItem
28346      */
28347     addTab : function(id, text, content, closable){
28348         var item = new Roo.TabPanelItem(this, id, text, closable);
28349         this.addTabItem(item);
28350         if(content){
28351             item.setContent(content);
28352         }
28353         return item;
28354     },
28355
28356     /**
28357      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28358      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28359      * @return {Roo.TabPanelItem}
28360      */
28361     getTab : function(id){
28362         return this.items[id];
28363     },
28364
28365     /**
28366      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28367      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28368      */
28369     hideTab : function(id){
28370         var t = this.items[id];
28371         if(!t.isHidden()){
28372            t.setHidden(true);
28373            this.hiddenCount++;
28374            this.autoSizeTabs();
28375         }
28376     },
28377
28378     /**
28379      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28380      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28381      */
28382     unhideTab : function(id){
28383         var t = this.items[id];
28384         if(t.isHidden()){
28385            t.setHidden(false);
28386            this.hiddenCount--;
28387            this.autoSizeTabs();
28388         }
28389     },
28390
28391     /**
28392      * Adds an existing {@link Roo.TabPanelItem}.
28393      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28394      */
28395     addTabItem : function(item){
28396         this.items[item.id] = item;
28397         this.items.push(item);
28398         if(this.resizeTabs){
28399            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28400            this.autoSizeTabs();
28401         }else{
28402             item.autoSize();
28403         }
28404     },
28405
28406     /**
28407      * Removes a {@link Roo.TabPanelItem}.
28408      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28409      */
28410     removeTab : function(id){
28411         var items = this.items;
28412         var tab = items[id];
28413         if(!tab) { return; }
28414         var index = items.indexOf(tab);
28415         if(this.active == tab && items.length > 1){
28416             var newTab = this.getNextAvailable(index);
28417             if(newTab) {
28418                 newTab.activate();
28419             }
28420         }
28421         this.stripEl.dom.removeChild(tab.pnode.dom);
28422         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28423             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28424         }
28425         items.splice(index, 1);
28426         delete this.items[tab.id];
28427         tab.fireEvent("close", tab);
28428         tab.purgeListeners();
28429         this.autoSizeTabs();
28430     },
28431
28432     getNextAvailable : function(start){
28433         var items = this.items;
28434         var index = start;
28435         // look for a next tab that will slide over to
28436         // replace the one being removed
28437         while(index < items.length){
28438             var item = items[++index];
28439             if(item && !item.isHidden()){
28440                 return item;
28441             }
28442         }
28443         // if one isn't found select the previous tab (on the left)
28444         index = start;
28445         while(index >= 0){
28446             var item = items[--index];
28447             if(item && !item.isHidden()){
28448                 return item;
28449             }
28450         }
28451         return null;
28452     },
28453
28454     /**
28455      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28456      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28457      */
28458     disableTab : function(id){
28459         var tab = this.items[id];
28460         if(tab && this.active != tab){
28461             tab.disable();
28462         }
28463     },
28464
28465     /**
28466      * Enables a {@link Roo.TabPanelItem} that is disabled.
28467      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28468      */
28469     enableTab : function(id){
28470         var tab = this.items[id];
28471         tab.enable();
28472     },
28473
28474     /**
28475      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28476      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28477      * @return {Roo.TabPanelItem} The TabPanelItem.
28478      */
28479     activate : function(id){
28480         var tab = this.items[id];
28481         if(!tab){
28482             return null;
28483         }
28484         if(tab == this.active || tab.disabled){
28485             return tab;
28486         }
28487         var e = {};
28488         this.fireEvent("beforetabchange", this, e, tab);
28489         if(e.cancel !== true && !tab.disabled){
28490             if(this.active){
28491                 this.active.hide();
28492             }
28493             this.active = this.items[id];
28494             this.active.show();
28495             this.fireEvent("tabchange", this, this.active);
28496         }
28497         return tab;
28498     },
28499
28500     /**
28501      * Gets the active {@link Roo.TabPanelItem}.
28502      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28503      */
28504     getActiveTab : function(){
28505         return this.active;
28506     },
28507
28508     /**
28509      * Updates the tab body element to fit the height of the container element
28510      * for overflow scrolling
28511      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28512      */
28513     syncHeight : function(targetHeight){
28514         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28515         var bm = this.bodyEl.getMargins();
28516         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28517         this.bodyEl.setHeight(newHeight);
28518         return newHeight;
28519     },
28520
28521     onResize : function(){
28522         if(this.monitorResize){
28523             this.autoSizeTabs();
28524         }
28525     },
28526
28527     /**
28528      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28529      */
28530     beginUpdate : function(){
28531         this.updating = true;
28532     },
28533
28534     /**
28535      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28536      */
28537     endUpdate : function(){
28538         this.updating = false;
28539         this.autoSizeTabs();
28540     },
28541
28542     /**
28543      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28544      */
28545     autoSizeTabs : function(){
28546         var count = this.items.length;
28547         var vcount = count - this.hiddenCount;
28548         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28549             return;
28550         }
28551         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28552         var availWidth = Math.floor(w / vcount);
28553         var b = this.stripBody;
28554         if(b.getWidth() > w){
28555             var tabs = this.items;
28556             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28557             if(availWidth < this.minTabWidth){
28558                 /*if(!this.sleft){    // incomplete scrolling code
28559                     this.createScrollButtons();
28560                 }
28561                 this.showScroll();
28562                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28563             }
28564         }else{
28565             if(this.currentTabWidth < this.preferredTabWidth){
28566                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28567             }
28568         }
28569     },
28570
28571     /**
28572      * Returns the number of tabs in this TabPanel.
28573      * @return {Number}
28574      */
28575      getCount : function(){
28576          return this.items.length;
28577      },
28578
28579     /**
28580      * Resizes all the tabs to the passed width
28581      * @param {Number} The new width
28582      */
28583     setTabWidth : function(width){
28584         this.currentTabWidth = width;
28585         for(var i = 0, len = this.items.length; i < len; i++) {
28586                 if(!this.items[i].isHidden()) {
28587                 this.items[i].setWidth(width);
28588             }
28589         }
28590     },
28591
28592     /**
28593      * Destroys this TabPanel
28594      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28595      */
28596     destroy : function(removeEl){
28597         Roo.EventManager.removeResizeListener(this.onResize, this);
28598         for(var i = 0, len = this.items.length; i < len; i++){
28599             this.items[i].purgeListeners();
28600         }
28601         if(removeEl === true){
28602             this.el.update("");
28603             this.el.remove();
28604         }
28605     }
28606 });
28607
28608 /**
28609  * @class Roo.TabPanelItem
28610  * @extends Roo.util.Observable
28611  * Represents an individual item (tab plus body) in a TabPanel.
28612  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28613  * @param {String} id The id of this TabPanelItem
28614  * @param {String} text The text for the tab of this TabPanelItem
28615  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28616  */
28617 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28618     /**
28619      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28620      * @type Roo.TabPanel
28621      */
28622     this.tabPanel = tabPanel;
28623     /**
28624      * The id for this TabPanelItem
28625      * @type String
28626      */
28627     this.id = id;
28628     /** @private */
28629     this.disabled = false;
28630     /** @private */
28631     this.text = text;
28632     /** @private */
28633     this.loaded = false;
28634     this.closable = closable;
28635
28636     /**
28637      * The body element for this TabPanelItem.
28638      * @type Roo.Element
28639      */
28640     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28641     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28642     this.bodyEl.setStyle("display", "block");
28643     this.bodyEl.setStyle("zoom", "1");
28644     this.hideAction();
28645
28646     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28647     /** @private */
28648     this.el = Roo.get(els.el, true);
28649     this.inner = Roo.get(els.inner, true);
28650     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28651     this.pnode = Roo.get(els.el.parentNode, true);
28652     this.el.on("mousedown", this.onTabMouseDown, this);
28653     this.el.on("click", this.onTabClick, this);
28654     /** @private */
28655     if(closable){
28656         var c = Roo.get(els.close, true);
28657         c.dom.title = this.closeText;
28658         c.addClassOnOver("close-over");
28659         c.on("click", this.closeClick, this);
28660      }
28661
28662     this.addEvents({
28663          /**
28664          * @event activate
28665          * Fires when this tab becomes the active tab.
28666          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28667          * @param {Roo.TabPanelItem} this
28668          */
28669         "activate": true,
28670         /**
28671          * @event beforeclose
28672          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28673          * @param {Roo.TabPanelItem} this
28674          * @param {Object} e Set cancel to true on this object to cancel the close.
28675          */
28676         "beforeclose": true,
28677         /**
28678          * @event close
28679          * Fires when this tab is closed.
28680          * @param {Roo.TabPanelItem} this
28681          */
28682          "close": true,
28683         /**
28684          * @event deactivate
28685          * Fires when this tab is no longer the active tab.
28686          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28687          * @param {Roo.TabPanelItem} this
28688          */
28689          "deactivate" : true
28690     });
28691     this.hidden = false;
28692
28693     Roo.TabPanelItem.superclass.constructor.call(this);
28694 };
28695
28696 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28697     purgeListeners : function(){
28698        Roo.util.Observable.prototype.purgeListeners.call(this);
28699        this.el.removeAllListeners();
28700     },
28701     /**
28702      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28703      */
28704     show : function(){
28705         this.pnode.addClass("on");
28706         this.showAction();
28707         if(Roo.isOpera){
28708             this.tabPanel.stripWrap.repaint();
28709         }
28710         this.fireEvent("activate", this.tabPanel, this);
28711     },
28712
28713     /**
28714      * Returns true if this tab is the active tab.
28715      * @return {Boolean}
28716      */
28717     isActive : function(){
28718         return this.tabPanel.getActiveTab() == this;
28719     },
28720
28721     /**
28722      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28723      */
28724     hide : function(){
28725         this.pnode.removeClass("on");
28726         this.hideAction();
28727         this.fireEvent("deactivate", this.tabPanel, this);
28728     },
28729
28730     hideAction : function(){
28731         this.bodyEl.hide();
28732         this.bodyEl.setStyle("position", "absolute");
28733         this.bodyEl.setLeft("-20000px");
28734         this.bodyEl.setTop("-20000px");
28735     },
28736
28737     showAction : function(){
28738         this.bodyEl.setStyle("position", "relative");
28739         this.bodyEl.setTop("");
28740         this.bodyEl.setLeft("");
28741         this.bodyEl.show();
28742     },
28743
28744     /**
28745      * Set the tooltip for the tab.
28746      * @param {String} tooltip The tab's tooltip
28747      */
28748     setTooltip : function(text){
28749         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28750             this.textEl.dom.qtip = text;
28751             this.textEl.dom.removeAttribute('title');
28752         }else{
28753             this.textEl.dom.title = text;
28754         }
28755     },
28756
28757     onTabClick : function(e){
28758         e.preventDefault();
28759         this.tabPanel.activate(this.id);
28760     },
28761
28762     onTabMouseDown : function(e){
28763         e.preventDefault();
28764         this.tabPanel.activate(this.id);
28765     },
28766
28767     getWidth : function(){
28768         return this.inner.getWidth();
28769     },
28770
28771     setWidth : function(width){
28772         var iwidth = width - this.pnode.getPadding("lr");
28773         this.inner.setWidth(iwidth);
28774         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28775         this.pnode.setWidth(width);
28776     },
28777
28778     /**
28779      * Show or hide the tab
28780      * @param {Boolean} hidden True to hide or false to show.
28781      */
28782     setHidden : function(hidden){
28783         this.hidden = hidden;
28784         this.pnode.setStyle("display", hidden ? "none" : "");
28785     },
28786
28787     /**
28788      * Returns true if this tab is "hidden"
28789      * @return {Boolean}
28790      */
28791     isHidden : function(){
28792         return this.hidden;
28793     },
28794
28795     /**
28796      * Returns the text for this tab
28797      * @return {String}
28798      */
28799     getText : function(){
28800         return this.text;
28801     },
28802
28803     autoSize : function(){
28804         //this.el.beginMeasure();
28805         this.textEl.setWidth(1);
28806         /*
28807          *  #2804 [new] Tabs in Roojs
28808          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28809          */
28810         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28811         //this.el.endMeasure();
28812     },
28813
28814     /**
28815      * Sets the text for the tab (Note: this also sets the tooltip text)
28816      * @param {String} text The tab's text and tooltip
28817      */
28818     setText : function(text){
28819         this.text = text;
28820         this.textEl.update(text);
28821         this.setTooltip(text);
28822         if(!this.tabPanel.resizeTabs){
28823             this.autoSize();
28824         }
28825     },
28826     /**
28827      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28828      */
28829     activate : function(){
28830         this.tabPanel.activate(this.id);
28831     },
28832
28833     /**
28834      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28835      */
28836     disable : function(){
28837         if(this.tabPanel.active != this){
28838             this.disabled = true;
28839             this.pnode.addClass("disabled");
28840         }
28841     },
28842
28843     /**
28844      * Enables this TabPanelItem if it was previously disabled.
28845      */
28846     enable : function(){
28847         this.disabled = false;
28848         this.pnode.removeClass("disabled");
28849     },
28850
28851     /**
28852      * Sets the content for this TabPanelItem.
28853      * @param {String} content The content
28854      * @param {Boolean} loadScripts true to look for and load scripts
28855      */
28856     setContent : function(content, loadScripts){
28857         this.bodyEl.update(content, loadScripts);
28858     },
28859
28860     /**
28861      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28862      * @return {Roo.UpdateManager} The UpdateManager
28863      */
28864     getUpdateManager : function(){
28865         return this.bodyEl.getUpdateManager();
28866     },
28867
28868     /**
28869      * Set a URL to be used to load the content for this TabPanelItem.
28870      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28871      * @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)
28872      * @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)
28873      * @return {Roo.UpdateManager} The UpdateManager
28874      */
28875     setUrl : function(url, params, loadOnce){
28876         if(this.refreshDelegate){
28877             this.un('activate', this.refreshDelegate);
28878         }
28879         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28880         this.on("activate", this.refreshDelegate);
28881         return this.bodyEl.getUpdateManager();
28882     },
28883
28884     /** @private */
28885     _handleRefresh : function(url, params, loadOnce){
28886         if(!loadOnce || !this.loaded){
28887             var updater = this.bodyEl.getUpdateManager();
28888             updater.update(url, params, this._setLoaded.createDelegate(this));
28889         }
28890     },
28891
28892     /**
28893      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
28894      *   Will fail silently if the setUrl method has not been called.
28895      *   This does not activate the panel, just updates its content.
28896      */
28897     refresh : function(){
28898         if(this.refreshDelegate){
28899            this.loaded = false;
28900            this.refreshDelegate();
28901         }
28902     },
28903
28904     /** @private */
28905     _setLoaded : function(){
28906         this.loaded = true;
28907     },
28908
28909     /** @private */
28910     closeClick : function(e){
28911         var o = {};
28912         e.stopEvent();
28913         this.fireEvent("beforeclose", this, o);
28914         if(o.cancel !== true){
28915             this.tabPanel.removeTab(this.id);
28916         }
28917     },
28918     /**
28919      * The text displayed in the tooltip for the close icon.
28920      * @type String
28921      */
28922     closeText : "Close this tab"
28923 });
28924
28925 /** @private */
28926 Roo.TabPanel.prototype.createStrip = function(container){
28927     var strip = document.createElement("div");
28928     strip.className = "x-tabs-wrap";
28929     container.appendChild(strip);
28930     return strip;
28931 };
28932 /** @private */
28933 Roo.TabPanel.prototype.createStripList = function(strip){
28934     // div wrapper for retard IE
28935     // returns the "tr" element.
28936     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
28937         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
28938         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
28939     return strip.firstChild.firstChild.firstChild.firstChild;
28940 };
28941 /** @private */
28942 Roo.TabPanel.prototype.createBody = function(container){
28943     var body = document.createElement("div");
28944     Roo.id(body, "tab-body");
28945     Roo.fly(body).addClass("x-tabs-body");
28946     container.appendChild(body);
28947     return body;
28948 };
28949 /** @private */
28950 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
28951     var body = Roo.getDom(id);
28952     if(!body){
28953         body = document.createElement("div");
28954         body.id = id;
28955     }
28956     Roo.fly(body).addClass("x-tabs-item-body");
28957     bodyEl.insertBefore(body, bodyEl.firstChild);
28958     return body;
28959 };
28960 /** @private */
28961 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
28962     var td = document.createElement("td");
28963     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
28964     //stripEl.appendChild(td);
28965     if(closable){
28966         td.className = "x-tabs-closable";
28967         if(!this.closeTpl){
28968             this.closeTpl = new Roo.Template(
28969                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28970                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
28971                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
28972             );
28973         }
28974         var el = this.closeTpl.overwrite(td, {"text": text});
28975         var close = el.getElementsByTagName("div")[0];
28976         var inner = el.getElementsByTagName("em")[0];
28977         return {"el": el, "close": close, "inner": inner};
28978     } else {
28979         if(!this.tabTpl){
28980             this.tabTpl = new Roo.Template(
28981                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28982                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
28983             );
28984         }
28985         var el = this.tabTpl.overwrite(td, {"text": text});
28986         var inner = el.getElementsByTagName("em")[0];
28987         return {"el": el, "inner": inner};
28988     }
28989 };/*
28990  * Based on:
28991  * Ext JS Library 1.1.1
28992  * Copyright(c) 2006-2007, Ext JS, LLC.
28993  *
28994  * Originally Released Under LGPL - original licence link has changed is not relivant.
28995  *
28996  * Fork - LGPL
28997  * <script type="text/javascript">
28998  */
28999
29000 /**
29001  * @class Roo.Button
29002  * @extends Roo.util.Observable
29003  * Simple Button class
29004  * @cfg {String} text The button text
29005  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29006  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29007  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29008  * @cfg {Object} scope The scope of the handler
29009  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29010  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29011  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29012  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29013  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29014  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29015    applies if enableToggle = true)
29016  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29017  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29018   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29019  * @constructor
29020  * Create a new button
29021  * @param {Object} config The config object
29022  */
29023 Roo.Button = function(renderTo, config)
29024 {
29025     if (!config) {
29026         config = renderTo;
29027         renderTo = config.renderTo || false;
29028     }
29029     
29030     Roo.apply(this, config);
29031     this.addEvents({
29032         /**
29033              * @event click
29034              * Fires when this button is clicked
29035              * @param {Button} this
29036              * @param {EventObject} e The click event
29037              */
29038             "click" : true,
29039         /**
29040              * @event toggle
29041              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29042              * @param {Button} this
29043              * @param {Boolean} pressed
29044              */
29045             "toggle" : true,
29046         /**
29047              * @event mouseover
29048              * Fires when the mouse hovers over the button
29049              * @param {Button} this
29050              * @param {Event} e The event object
29051              */
29052         'mouseover' : true,
29053         /**
29054              * @event mouseout
29055              * Fires when the mouse exits the button
29056              * @param {Button} this
29057              * @param {Event} e The event object
29058              */
29059         'mouseout': true,
29060          /**
29061              * @event render
29062              * Fires when the button is rendered
29063              * @param {Button} this
29064              */
29065         'render': true
29066     });
29067     if(this.menu){
29068         this.menu = Roo.menu.MenuMgr.get(this.menu);
29069     }
29070     // register listeners first!!  - so render can be captured..
29071     Roo.util.Observable.call(this);
29072     if(renderTo){
29073         this.render(renderTo);
29074     }
29075     
29076   
29077 };
29078
29079 Roo.extend(Roo.Button, Roo.util.Observable, {
29080     /**
29081      * 
29082      */
29083     
29084     /**
29085      * Read-only. True if this button is hidden
29086      * @type Boolean
29087      */
29088     hidden : false,
29089     /**
29090      * Read-only. True if this button is disabled
29091      * @type Boolean
29092      */
29093     disabled : false,
29094     /**
29095      * Read-only. True if this button is pressed (only if enableToggle = true)
29096      * @type Boolean
29097      */
29098     pressed : false,
29099
29100     /**
29101      * @cfg {Number} tabIndex 
29102      * The DOM tabIndex for this button (defaults to undefined)
29103      */
29104     tabIndex : undefined,
29105
29106     /**
29107      * @cfg {Boolean} enableToggle
29108      * True to enable pressed/not pressed toggling (defaults to false)
29109      */
29110     enableToggle: false,
29111     /**
29112      * @cfg {Mixed} menu
29113      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29114      */
29115     menu : undefined,
29116     /**
29117      * @cfg {String} menuAlign
29118      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29119      */
29120     menuAlign : "tl-bl?",
29121
29122     /**
29123      * @cfg {String} iconCls
29124      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29125      */
29126     iconCls : undefined,
29127     /**
29128      * @cfg {String} type
29129      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29130      */
29131     type : 'button',
29132
29133     // private
29134     menuClassTarget: 'tr',
29135
29136     /**
29137      * @cfg {String} clickEvent
29138      * The type of event to map to the button's event handler (defaults to 'click')
29139      */
29140     clickEvent : 'click',
29141
29142     /**
29143      * @cfg {Boolean} handleMouseEvents
29144      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29145      */
29146     handleMouseEvents : true,
29147
29148     /**
29149      * @cfg {String} tooltipType
29150      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29151      */
29152     tooltipType : 'qtip',
29153
29154     /**
29155      * @cfg {String} cls
29156      * A CSS class to apply to the button's main element.
29157      */
29158     
29159     /**
29160      * @cfg {Roo.Template} template (Optional)
29161      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29162      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29163      * require code modifications if required elements (e.g. a button) aren't present.
29164      */
29165
29166     // private
29167     render : function(renderTo){
29168         var btn;
29169         if(this.hideParent){
29170             this.parentEl = Roo.get(renderTo);
29171         }
29172         if(!this.dhconfig){
29173             if(!this.template){
29174                 if(!Roo.Button.buttonTemplate){
29175                     // hideous table template
29176                     Roo.Button.buttonTemplate = new Roo.Template(
29177                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29178                         '<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>',
29179                         "</tr></tbody></table>");
29180                 }
29181                 this.template = Roo.Button.buttonTemplate;
29182             }
29183             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29184             var btnEl = btn.child("button:first");
29185             btnEl.on('focus', this.onFocus, this);
29186             btnEl.on('blur', this.onBlur, this);
29187             if(this.cls){
29188                 btn.addClass(this.cls);
29189             }
29190             if(this.icon){
29191                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29192             }
29193             if(this.iconCls){
29194                 btnEl.addClass(this.iconCls);
29195                 if(!this.cls){
29196                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29197                 }
29198             }
29199             if(this.tabIndex !== undefined){
29200                 btnEl.dom.tabIndex = this.tabIndex;
29201             }
29202             if(this.tooltip){
29203                 if(typeof this.tooltip == 'object'){
29204                     Roo.QuickTips.tips(Roo.apply({
29205                           target: btnEl.id
29206                     }, this.tooltip));
29207                 } else {
29208                     btnEl.dom[this.tooltipType] = this.tooltip;
29209                 }
29210             }
29211         }else{
29212             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29213         }
29214         this.el = btn;
29215         if(this.id){
29216             this.el.dom.id = this.el.id = this.id;
29217         }
29218         if(this.menu){
29219             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29220             this.menu.on("show", this.onMenuShow, this);
29221             this.menu.on("hide", this.onMenuHide, this);
29222         }
29223         btn.addClass("x-btn");
29224         if(Roo.isIE && !Roo.isIE7){
29225             this.autoWidth.defer(1, this);
29226         }else{
29227             this.autoWidth();
29228         }
29229         if(this.handleMouseEvents){
29230             btn.on("mouseover", this.onMouseOver, this);
29231             btn.on("mouseout", this.onMouseOut, this);
29232             btn.on("mousedown", this.onMouseDown, this);
29233         }
29234         btn.on(this.clickEvent, this.onClick, this);
29235         //btn.on("mouseup", this.onMouseUp, this);
29236         if(this.hidden){
29237             this.hide();
29238         }
29239         if(this.disabled){
29240             this.disable();
29241         }
29242         Roo.ButtonToggleMgr.register(this);
29243         if(this.pressed){
29244             this.el.addClass("x-btn-pressed");
29245         }
29246         if(this.repeat){
29247             var repeater = new Roo.util.ClickRepeater(btn,
29248                 typeof this.repeat == "object" ? this.repeat : {}
29249             );
29250             repeater.on("click", this.onClick,  this);
29251         }
29252         
29253         this.fireEvent('render', this);
29254         
29255     },
29256     /**
29257      * Returns the button's underlying element
29258      * @return {Roo.Element} The element
29259      */
29260     getEl : function(){
29261         return this.el;  
29262     },
29263     
29264     /**
29265      * Destroys this Button and removes any listeners.
29266      */
29267     destroy : function(){
29268         Roo.ButtonToggleMgr.unregister(this);
29269         this.el.removeAllListeners();
29270         this.purgeListeners();
29271         this.el.remove();
29272     },
29273
29274     // private
29275     autoWidth : function(){
29276         if(this.el){
29277             this.el.setWidth("auto");
29278             if(Roo.isIE7 && Roo.isStrict){
29279                 var ib = this.el.child('button');
29280                 if(ib && ib.getWidth() > 20){
29281                     ib.clip();
29282                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29283                 }
29284             }
29285             if(this.minWidth){
29286                 if(this.hidden){
29287                     this.el.beginMeasure();
29288                 }
29289                 if(this.el.getWidth() < this.minWidth){
29290                     this.el.setWidth(this.minWidth);
29291                 }
29292                 if(this.hidden){
29293                     this.el.endMeasure();
29294                 }
29295             }
29296         }
29297     },
29298
29299     /**
29300      * Assigns this button's click handler
29301      * @param {Function} handler The function to call when the button is clicked
29302      * @param {Object} scope (optional) Scope for the function passed in
29303      */
29304     setHandler : function(handler, scope){
29305         this.handler = handler;
29306         this.scope = scope;  
29307     },
29308     
29309     /**
29310      * Sets this button's text
29311      * @param {String} text The button text
29312      */
29313     setText : function(text){
29314         this.text = text;
29315         if(this.el){
29316             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29317         }
29318         this.autoWidth();
29319     },
29320     
29321     /**
29322      * Gets the text for this button
29323      * @return {String} The button text
29324      */
29325     getText : function(){
29326         return this.text;  
29327     },
29328     
29329     /**
29330      * Show this button
29331      */
29332     show: function(){
29333         this.hidden = false;
29334         if(this.el){
29335             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29336         }
29337     },
29338     
29339     /**
29340      * Hide this button
29341      */
29342     hide: function(){
29343         this.hidden = true;
29344         if(this.el){
29345             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29346         }
29347     },
29348     
29349     /**
29350      * Convenience function for boolean show/hide
29351      * @param {Boolean} visible True to show, false to hide
29352      */
29353     setVisible: function(visible){
29354         if(visible) {
29355             this.show();
29356         }else{
29357             this.hide();
29358         }
29359     },
29360     
29361     /**
29362      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29363      * @param {Boolean} state (optional) Force a particular state
29364      */
29365     toggle : function(state){
29366         state = state === undefined ? !this.pressed : state;
29367         if(state != this.pressed){
29368             if(state){
29369                 this.el.addClass("x-btn-pressed");
29370                 this.pressed = true;
29371                 this.fireEvent("toggle", this, true);
29372             }else{
29373                 this.el.removeClass("x-btn-pressed");
29374                 this.pressed = false;
29375                 this.fireEvent("toggle", this, false);
29376             }
29377             if(this.toggleHandler){
29378                 this.toggleHandler.call(this.scope || this, this, state);
29379             }
29380         }
29381     },
29382     
29383     /**
29384      * Focus the button
29385      */
29386     focus : function(){
29387         this.el.child('button:first').focus();
29388     },
29389     
29390     /**
29391      * Disable this button
29392      */
29393     disable : function(){
29394         if(this.el){
29395             this.el.addClass("x-btn-disabled");
29396         }
29397         this.disabled = true;
29398     },
29399     
29400     /**
29401      * Enable this button
29402      */
29403     enable : function(){
29404         if(this.el){
29405             this.el.removeClass("x-btn-disabled");
29406         }
29407         this.disabled = false;
29408     },
29409
29410     /**
29411      * Convenience function for boolean enable/disable
29412      * @param {Boolean} enabled True to enable, false to disable
29413      */
29414     setDisabled : function(v){
29415         this[v !== true ? "enable" : "disable"]();
29416     },
29417
29418     // private
29419     onClick : function(e)
29420     {
29421         if(e){
29422             e.preventDefault();
29423         }
29424         if(e.button != 0){
29425             return;
29426         }
29427         if(!this.disabled){
29428             if(this.enableToggle){
29429                 this.toggle();
29430             }
29431             if(this.menu && !this.menu.isVisible()){
29432                 this.menu.show(this.el, this.menuAlign);
29433             }
29434             this.fireEvent("click", this, e);
29435             if(this.handler){
29436                 this.el.removeClass("x-btn-over");
29437                 this.handler.call(this.scope || this, this, e);
29438             }
29439         }
29440     },
29441     // private
29442     onMouseOver : function(e){
29443         if(!this.disabled){
29444             this.el.addClass("x-btn-over");
29445             this.fireEvent('mouseover', this, e);
29446         }
29447     },
29448     // private
29449     onMouseOut : function(e){
29450         if(!e.within(this.el,  true)){
29451             this.el.removeClass("x-btn-over");
29452             this.fireEvent('mouseout', this, e);
29453         }
29454     },
29455     // private
29456     onFocus : function(e){
29457         if(!this.disabled){
29458             this.el.addClass("x-btn-focus");
29459         }
29460     },
29461     // private
29462     onBlur : function(e){
29463         this.el.removeClass("x-btn-focus");
29464     },
29465     // private
29466     onMouseDown : function(e){
29467         if(!this.disabled && e.button == 0){
29468             this.el.addClass("x-btn-click");
29469             Roo.get(document).on('mouseup', this.onMouseUp, this);
29470         }
29471     },
29472     // private
29473     onMouseUp : function(e){
29474         if(e.button == 0){
29475             this.el.removeClass("x-btn-click");
29476             Roo.get(document).un('mouseup', this.onMouseUp, this);
29477         }
29478     },
29479     // private
29480     onMenuShow : function(e){
29481         this.el.addClass("x-btn-menu-active");
29482     },
29483     // private
29484     onMenuHide : function(e){
29485         this.el.removeClass("x-btn-menu-active");
29486     }   
29487 });
29488
29489 // Private utility class used by Button
29490 Roo.ButtonToggleMgr = function(){
29491    var groups = {};
29492    
29493    function toggleGroup(btn, state){
29494        if(state){
29495            var g = groups[btn.toggleGroup];
29496            for(var i = 0, l = g.length; i < l; i++){
29497                if(g[i] != btn){
29498                    g[i].toggle(false);
29499                }
29500            }
29501        }
29502    }
29503    
29504    return {
29505        register : function(btn){
29506            if(!btn.toggleGroup){
29507                return;
29508            }
29509            var g = groups[btn.toggleGroup];
29510            if(!g){
29511                g = groups[btn.toggleGroup] = [];
29512            }
29513            g.push(btn);
29514            btn.on("toggle", toggleGroup);
29515        },
29516        
29517        unregister : function(btn){
29518            if(!btn.toggleGroup){
29519                return;
29520            }
29521            var g = groups[btn.toggleGroup];
29522            if(g){
29523                g.remove(btn);
29524                btn.un("toggle", toggleGroup);
29525            }
29526        }
29527    };
29528 }();/*
29529  * Based on:
29530  * Ext JS Library 1.1.1
29531  * Copyright(c) 2006-2007, Ext JS, LLC.
29532  *
29533  * Originally Released Under LGPL - original licence link has changed is not relivant.
29534  *
29535  * Fork - LGPL
29536  * <script type="text/javascript">
29537  */
29538  
29539 /**
29540  * @class Roo.SplitButton
29541  * @extends Roo.Button
29542  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29543  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29544  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29545  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29546  * @cfg {String} arrowTooltip The title attribute of the arrow
29547  * @constructor
29548  * Create a new menu button
29549  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29550  * @param {Object} config The config object
29551  */
29552 Roo.SplitButton = function(renderTo, config){
29553     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29554     /**
29555      * @event arrowclick
29556      * Fires when this button's arrow is clicked
29557      * @param {SplitButton} this
29558      * @param {EventObject} e The click event
29559      */
29560     this.addEvents({"arrowclick":true});
29561 };
29562
29563 Roo.extend(Roo.SplitButton, Roo.Button, {
29564     render : function(renderTo){
29565         // this is one sweet looking template!
29566         var tpl = new Roo.Template(
29567             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29568             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29569             '<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>',
29570             "</tbody></table></td><td>",
29571             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29572             '<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>',
29573             "</tbody></table></td></tr></table>"
29574         );
29575         var btn = tpl.append(renderTo, [this.text, this.type], true);
29576         var btnEl = btn.child("button");
29577         if(this.cls){
29578             btn.addClass(this.cls);
29579         }
29580         if(this.icon){
29581             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29582         }
29583         if(this.iconCls){
29584             btnEl.addClass(this.iconCls);
29585             if(!this.cls){
29586                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29587             }
29588         }
29589         this.el = btn;
29590         if(this.handleMouseEvents){
29591             btn.on("mouseover", this.onMouseOver, this);
29592             btn.on("mouseout", this.onMouseOut, this);
29593             btn.on("mousedown", this.onMouseDown, this);
29594             btn.on("mouseup", this.onMouseUp, this);
29595         }
29596         btn.on(this.clickEvent, this.onClick, this);
29597         if(this.tooltip){
29598             if(typeof this.tooltip == 'object'){
29599                 Roo.QuickTips.tips(Roo.apply({
29600                       target: btnEl.id
29601                 }, this.tooltip));
29602             } else {
29603                 btnEl.dom[this.tooltipType] = this.tooltip;
29604             }
29605         }
29606         if(this.arrowTooltip){
29607             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29608         }
29609         if(this.hidden){
29610             this.hide();
29611         }
29612         if(this.disabled){
29613             this.disable();
29614         }
29615         if(this.pressed){
29616             this.el.addClass("x-btn-pressed");
29617         }
29618         if(Roo.isIE && !Roo.isIE7){
29619             this.autoWidth.defer(1, this);
29620         }else{
29621             this.autoWidth();
29622         }
29623         if(this.menu){
29624             this.menu.on("show", this.onMenuShow, this);
29625             this.menu.on("hide", this.onMenuHide, this);
29626         }
29627         this.fireEvent('render', this);
29628     },
29629
29630     // private
29631     autoWidth : function(){
29632         if(this.el){
29633             var tbl = this.el.child("table:first");
29634             var tbl2 = this.el.child("table:last");
29635             this.el.setWidth("auto");
29636             tbl.setWidth("auto");
29637             if(Roo.isIE7 && Roo.isStrict){
29638                 var ib = this.el.child('button:first');
29639                 if(ib && ib.getWidth() > 20){
29640                     ib.clip();
29641                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29642                 }
29643             }
29644             if(this.minWidth){
29645                 if(this.hidden){
29646                     this.el.beginMeasure();
29647                 }
29648                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29649                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29650                 }
29651                 if(this.hidden){
29652                     this.el.endMeasure();
29653                 }
29654             }
29655             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29656         } 
29657     },
29658     /**
29659      * Sets this button's click handler
29660      * @param {Function} handler The function to call when the button is clicked
29661      * @param {Object} scope (optional) Scope for the function passed above
29662      */
29663     setHandler : function(handler, scope){
29664         this.handler = handler;
29665         this.scope = scope;  
29666     },
29667     
29668     /**
29669      * Sets this button's arrow click handler
29670      * @param {Function} handler The function to call when the arrow is clicked
29671      * @param {Object} scope (optional) Scope for the function passed above
29672      */
29673     setArrowHandler : function(handler, scope){
29674         this.arrowHandler = handler;
29675         this.scope = scope;  
29676     },
29677     
29678     /**
29679      * Focus the button
29680      */
29681     focus : function(){
29682         if(this.el){
29683             this.el.child("button:first").focus();
29684         }
29685     },
29686
29687     // private
29688     onClick : function(e){
29689         e.preventDefault();
29690         if(!this.disabled){
29691             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29692                 if(this.menu && !this.menu.isVisible()){
29693                     this.menu.show(this.el, this.menuAlign);
29694                 }
29695                 this.fireEvent("arrowclick", this, e);
29696                 if(this.arrowHandler){
29697                     this.arrowHandler.call(this.scope || this, this, e);
29698                 }
29699             }else{
29700                 this.fireEvent("click", this, e);
29701                 if(this.handler){
29702                     this.handler.call(this.scope || this, this, e);
29703                 }
29704             }
29705         }
29706     },
29707     // private
29708     onMouseDown : function(e){
29709         if(!this.disabled){
29710             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29711         }
29712     },
29713     // private
29714     onMouseUp : function(e){
29715         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29716     }   
29717 });
29718
29719
29720 // backwards compat
29721 Roo.MenuButton = Roo.SplitButton;/*
29722  * Based on:
29723  * Ext JS Library 1.1.1
29724  * Copyright(c) 2006-2007, Ext JS, LLC.
29725  *
29726  * Originally Released Under LGPL - original licence link has changed is not relivant.
29727  *
29728  * Fork - LGPL
29729  * <script type="text/javascript">
29730  */
29731
29732 /**
29733  * @class Roo.Toolbar
29734  * Basic Toolbar class.
29735  * @constructor
29736  * Creates a new Toolbar
29737  * @param {Object} container The config object
29738  */ 
29739 Roo.Toolbar = function(container, buttons, config)
29740 {
29741     /// old consturctor format still supported..
29742     if(container instanceof Array){ // omit the container for later rendering
29743         buttons = container;
29744         config = buttons;
29745         container = null;
29746     }
29747     if (typeof(container) == 'object' && container.xtype) {
29748         config = container;
29749         container = config.container;
29750         buttons = config.buttons || []; // not really - use items!!
29751     }
29752     var xitems = [];
29753     if (config && config.items) {
29754         xitems = config.items;
29755         delete config.items;
29756     }
29757     Roo.apply(this, config);
29758     this.buttons = buttons;
29759     
29760     if(container){
29761         this.render(container);
29762     }
29763     this.xitems = xitems;
29764     Roo.each(xitems, function(b) {
29765         this.add(b);
29766     }, this);
29767     
29768 };
29769
29770 Roo.Toolbar.prototype = {
29771     /**
29772      * @cfg {Array} items
29773      * array of button configs or elements to add (will be converted to a MixedCollection)
29774      */
29775     
29776     /**
29777      * @cfg {String/HTMLElement/Element} container
29778      * The id or element that will contain the toolbar
29779      */
29780     // private
29781     render : function(ct){
29782         this.el = Roo.get(ct);
29783         if(this.cls){
29784             this.el.addClass(this.cls);
29785         }
29786         // using a table allows for vertical alignment
29787         // 100% width is needed by Safari...
29788         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29789         this.tr = this.el.child("tr", true);
29790         var autoId = 0;
29791         this.items = new Roo.util.MixedCollection(false, function(o){
29792             return o.id || ("item" + (++autoId));
29793         });
29794         if(this.buttons){
29795             this.add.apply(this, this.buttons);
29796             delete this.buttons;
29797         }
29798     },
29799
29800     /**
29801      * Adds element(s) to the toolbar -- this function takes a variable number of 
29802      * arguments of mixed type and adds them to the toolbar.
29803      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29804      * <ul>
29805      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29806      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29807      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29808      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29809      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29810      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29811      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29812      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29813      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29814      * </ul>
29815      * @param {Mixed} arg2
29816      * @param {Mixed} etc.
29817      */
29818     add : function(){
29819         var a = arguments, l = a.length;
29820         for(var i = 0; i < l; i++){
29821             this._add(a[i]);
29822         }
29823     },
29824     // private..
29825     _add : function(el) {
29826         
29827         if (el.xtype) {
29828             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29829         }
29830         
29831         if (el.applyTo){ // some kind of form field
29832             return this.addField(el);
29833         } 
29834         if (el.render){ // some kind of Toolbar.Item
29835             return this.addItem(el);
29836         }
29837         if (typeof el == "string"){ // string
29838             if(el == "separator" || el == "-"){
29839                 return this.addSeparator();
29840             }
29841             if (el == " "){
29842                 return this.addSpacer();
29843             }
29844             if(el == "->"){
29845                 return this.addFill();
29846             }
29847             return this.addText(el);
29848             
29849         }
29850         if(el.tagName){ // element
29851             return this.addElement(el);
29852         }
29853         if(typeof el == "object"){ // must be button config?
29854             return this.addButton(el);
29855         }
29856         // and now what?!?!
29857         return false;
29858         
29859     },
29860     
29861     /**
29862      * Add an Xtype element
29863      * @param {Object} xtype Xtype Object
29864      * @return {Object} created Object
29865      */
29866     addxtype : function(e){
29867         return this.add(e);  
29868     },
29869     
29870     /**
29871      * Returns the Element for this toolbar.
29872      * @return {Roo.Element}
29873      */
29874     getEl : function(){
29875         return this.el;  
29876     },
29877     
29878     /**
29879      * Adds a separator
29880      * @return {Roo.Toolbar.Item} The separator item
29881      */
29882     addSeparator : function(){
29883         return this.addItem(new Roo.Toolbar.Separator());
29884     },
29885
29886     /**
29887      * Adds a spacer element
29888      * @return {Roo.Toolbar.Spacer} The spacer item
29889      */
29890     addSpacer : function(){
29891         return this.addItem(new Roo.Toolbar.Spacer());
29892     },
29893
29894     /**
29895      * Adds a fill element that forces subsequent additions to the right side of the toolbar
29896      * @return {Roo.Toolbar.Fill} The fill item
29897      */
29898     addFill : function(){
29899         return this.addItem(new Roo.Toolbar.Fill());
29900     },
29901
29902     /**
29903      * Adds any standard HTML element to the toolbar
29904      * @param {String/HTMLElement/Element} el The element or id of the element to add
29905      * @return {Roo.Toolbar.Item} The element's item
29906      */
29907     addElement : function(el){
29908         return this.addItem(new Roo.Toolbar.Item(el));
29909     },
29910     /**
29911      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
29912      * @type Roo.util.MixedCollection  
29913      */
29914     items : false,
29915      
29916     /**
29917      * Adds any Toolbar.Item or subclass
29918      * @param {Roo.Toolbar.Item} item
29919      * @return {Roo.Toolbar.Item} The item
29920      */
29921     addItem : function(item){
29922         var td = this.nextBlock();
29923         item.render(td);
29924         this.items.add(item);
29925         return item;
29926     },
29927     
29928     /**
29929      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
29930      * @param {Object/Array} config A button config or array of configs
29931      * @return {Roo.Toolbar.Button/Array}
29932      */
29933     addButton : function(config){
29934         if(config instanceof Array){
29935             var buttons = [];
29936             for(var i = 0, len = config.length; i < len; i++) {
29937                 buttons.push(this.addButton(config[i]));
29938             }
29939             return buttons;
29940         }
29941         var b = config;
29942         if(!(config instanceof Roo.Toolbar.Button)){
29943             b = config.split ?
29944                 new Roo.Toolbar.SplitButton(config) :
29945                 new Roo.Toolbar.Button(config);
29946         }
29947         var td = this.nextBlock();
29948         b.render(td);
29949         this.items.add(b);
29950         return b;
29951     },
29952     
29953     /**
29954      * Adds text to the toolbar
29955      * @param {String} text The text to add
29956      * @return {Roo.Toolbar.Item} The element's item
29957      */
29958     addText : function(text){
29959         return this.addItem(new Roo.Toolbar.TextItem(text));
29960     },
29961     
29962     /**
29963      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
29964      * @param {Number} index The index where the item is to be inserted
29965      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
29966      * @return {Roo.Toolbar.Button/Item}
29967      */
29968     insertButton : function(index, item){
29969         if(item instanceof Array){
29970             var buttons = [];
29971             for(var i = 0, len = item.length; i < len; i++) {
29972                buttons.push(this.insertButton(index + i, item[i]));
29973             }
29974             return buttons;
29975         }
29976         if (!(item instanceof Roo.Toolbar.Button)){
29977            item = new Roo.Toolbar.Button(item);
29978         }
29979         var td = document.createElement("td");
29980         this.tr.insertBefore(td, this.tr.childNodes[index]);
29981         item.render(td);
29982         this.items.insert(index, item);
29983         return item;
29984     },
29985     
29986     /**
29987      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
29988      * @param {Object} config
29989      * @return {Roo.Toolbar.Item} The element's item
29990      */
29991     addDom : function(config, returnEl){
29992         var td = this.nextBlock();
29993         Roo.DomHelper.overwrite(td, config);
29994         var ti = new Roo.Toolbar.Item(td.firstChild);
29995         ti.render(td);
29996         this.items.add(ti);
29997         return ti;
29998     },
29999
30000     /**
30001      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30002      * @type Roo.util.MixedCollection  
30003      */
30004     fields : false,
30005     
30006     /**
30007      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30008      * Note: the field should not have been rendered yet. For a field that has already been
30009      * rendered, use {@link #addElement}.
30010      * @param {Roo.form.Field} field
30011      * @return {Roo.ToolbarItem}
30012      */
30013      
30014       
30015     addField : function(field) {
30016         if (!this.fields) {
30017             var autoId = 0;
30018             this.fields = new Roo.util.MixedCollection(false, function(o){
30019                 return o.id || ("item" + (++autoId));
30020             });
30021
30022         }
30023         
30024         var td = this.nextBlock();
30025         field.render(td);
30026         var ti = new Roo.Toolbar.Item(td.firstChild);
30027         ti.render(td);
30028         this.items.add(ti);
30029         this.fields.add(field);
30030         return ti;
30031     },
30032     /**
30033      * Hide the toolbar
30034      * @method hide
30035      */
30036      
30037       
30038     hide : function()
30039     {
30040         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30041         this.el.child('div').hide();
30042     },
30043     /**
30044      * Show the toolbar
30045      * @method show
30046      */
30047     show : function()
30048     {
30049         this.el.child('div').show();
30050     },
30051       
30052     // private
30053     nextBlock : function(){
30054         var td = document.createElement("td");
30055         this.tr.appendChild(td);
30056         return td;
30057     },
30058
30059     // private
30060     destroy : function(){
30061         if(this.items){ // rendered?
30062             Roo.destroy.apply(Roo, this.items.items);
30063         }
30064         if(this.fields){ // rendered?
30065             Roo.destroy.apply(Roo, this.fields.items);
30066         }
30067         Roo.Element.uncache(this.el, this.tr);
30068     }
30069 };
30070
30071 /**
30072  * @class Roo.Toolbar.Item
30073  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30074  * @constructor
30075  * Creates a new Item
30076  * @param {HTMLElement} el 
30077  */
30078 Roo.Toolbar.Item = function(el){
30079     var cfg = {};
30080     if (typeof (el.xtype) != 'undefined') {
30081         cfg = el;
30082         el = cfg.el;
30083     }
30084     
30085     this.el = Roo.getDom(el);
30086     this.id = Roo.id(this.el);
30087     this.hidden = false;
30088     
30089     this.addEvents({
30090          /**
30091              * @event render
30092              * Fires when the button is rendered
30093              * @param {Button} this
30094              */
30095         'render': true
30096     });
30097     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30098 };
30099 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30100 //Roo.Toolbar.Item.prototype = {
30101     
30102     /**
30103      * Get this item's HTML Element
30104      * @return {HTMLElement}
30105      */
30106     getEl : function(){
30107        return this.el;  
30108     },
30109
30110     // private
30111     render : function(td){
30112         
30113          this.td = td;
30114         td.appendChild(this.el);
30115         
30116         this.fireEvent('render', this);
30117     },
30118     
30119     /**
30120      * Removes and destroys this item.
30121      */
30122     destroy : function(){
30123         this.td.parentNode.removeChild(this.td);
30124     },
30125     
30126     /**
30127      * Shows this item.
30128      */
30129     show: function(){
30130         this.hidden = false;
30131         this.td.style.display = "";
30132     },
30133     
30134     /**
30135      * Hides this item.
30136      */
30137     hide: function(){
30138         this.hidden = true;
30139         this.td.style.display = "none";
30140     },
30141     
30142     /**
30143      * Convenience function for boolean show/hide.
30144      * @param {Boolean} visible true to show/false to hide
30145      */
30146     setVisible: function(visible){
30147         if(visible) {
30148             this.show();
30149         }else{
30150             this.hide();
30151         }
30152     },
30153     
30154     /**
30155      * Try to focus this item.
30156      */
30157     focus : function(){
30158         Roo.fly(this.el).focus();
30159     },
30160     
30161     /**
30162      * Disables this item.
30163      */
30164     disable : function(){
30165         Roo.fly(this.td).addClass("x-item-disabled");
30166         this.disabled = true;
30167         this.el.disabled = true;
30168     },
30169     
30170     /**
30171      * Enables this item.
30172      */
30173     enable : function(){
30174         Roo.fly(this.td).removeClass("x-item-disabled");
30175         this.disabled = false;
30176         this.el.disabled = false;
30177     }
30178 });
30179
30180
30181 /**
30182  * @class Roo.Toolbar.Separator
30183  * @extends Roo.Toolbar.Item
30184  * A simple toolbar separator class
30185  * @constructor
30186  * Creates a new Separator
30187  */
30188 Roo.Toolbar.Separator = function(cfg){
30189     
30190     var s = document.createElement("span");
30191     s.className = "ytb-sep";
30192     if (cfg) {
30193         cfg.el = s;
30194     }
30195     
30196     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30197 };
30198 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30199     enable:Roo.emptyFn,
30200     disable:Roo.emptyFn,
30201     focus:Roo.emptyFn
30202 });
30203
30204 /**
30205  * @class Roo.Toolbar.Spacer
30206  * @extends Roo.Toolbar.Item
30207  * A simple element that adds extra horizontal space to a toolbar.
30208  * @constructor
30209  * Creates a new Spacer
30210  */
30211 Roo.Toolbar.Spacer = function(cfg){
30212     var s = document.createElement("div");
30213     s.className = "ytb-spacer";
30214     if (cfg) {
30215         cfg.el = s;
30216     }
30217     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30218 };
30219 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30220     enable:Roo.emptyFn,
30221     disable:Roo.emptyFn,
30222     focus:Roo.emptyFn
30223 });
30224
30225 /**
30226  * @class Roo.Toolbar.Fill
30227  * @extends Roo.Toolbar.Spacer
30228  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30229  * @constructor
30230  * Creates a new Spacer
30231  */
30232 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30233     // private
30234     render : function(td){
30235         td.style.width = '100%';
30236         Roo.Toolbar.Fill.superclass.render.call(this, td);
30237     }
30238 });
30239
30240 /**
30241  * @class Roo.Toolbar.TextItem
30242  * @extends Roo.Toolbar.Item
30243  * A simple class that renders text directly into a toolbar.
30244  * @constructor
30245  * Creates a new TextItem
30246  * @param {String} text
30247  */
30248 Roo.Toolbar.TextItem = function(cfg){
30249     var  text = cfg || "";
30250     if (typeof(cfg) == 'object') {
30251         text = cfg.text || "";
30252     }  else {
30253         cfg = null;
30254     }
30255     var s = document.createElement("span");
30256     s.className = "ytb-text";
30257     s.innerHTML = text;
30258     if (cfg) {
30259         cfg.el  = s;
30260     }
30261     
30262     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30263 };
30264 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30265     
30266      
30267     enable:Roo.emptyFn,
30268     disable:Roo.emptyFn,
30269     focus:Roo.emptyFn
30270 });
30271
30272 /**
30273  * @class Roo.Toolbar.Button
30274  * @extends Roo.Button
30275  * A button that renders into a toolbar.
30276  * @constructor
30277  * Creates a new Button
30278  * @param {Object} config A standard {@link Roo.Button} config object
30279  */
30280 Roo.Toolbar.Button = function(config){
30281     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30282 };
30283 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30284     render : function(td){
30285         this.td = td;
30286         Roo.Toolbar.Button.superclass.render.call(this, td);
30287     },
30288     
30289     /**
30290      * Removes and destroys this button
30291      */
30292     destroy : function(){
30293         Roo.Toolbar.Button.superclass.destroy.call(this);
30294         this.td.parentNode.removeChild(this.td);
30295     },
30296     
30297     /**
30298      * Shows this button
30299      */
30300     show: function(){
30301         this.hidden = false;
30302         this.td.style.display = "";
30303     },
30304     
30305     /**
30306      * Hides this button
30307      */
30308     hide: function(){
30309         this.hidden = true;
30310         this.td.style.display = "none";
30311     },
30312
30313     /**
30314      * Disables this item
30315      */
30316     disable : function(){
30317         Roo.fly(this.td).addClass("x-item-disabled");
30318         this.disabled = true;
30319     },
30320
30321     /**
30322      * Enables this item
30323      */
30324     enable : function(){
30325         Roo.fly(this.td).removeClass("x-item-disabled");
30326         this.disabled = false;
30327     }
30328 });
30329 // backwards compat
30330 Roo.ToolbarButton = Roo.Toolbar.Button;
30331
30332 /**
30333  * @class Roo.Toolbar.SplitButton
30334  * @extends Roo.SplitButton
30335  * A menu button that renders into a toolbar.
30336  * @constructor
30337  * Creates a new SplitButton
30338  * @param {Object} config A standard {@link Roo.SplitButton} config object
30339  */
30340 Roo.Toolbar.SplitButton = function(config){
30341     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30342 };
30343 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30344     render : function(td){
30345         this.td = td;
30346         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30347     },
30348     
30349     /**
30350      * Removes and destroys this button
30351      */
30352     destroy : function(){
30353         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30354         this.td.parentNode.removeChild(this.td);
30355     },
30356     
30357     /**
30358      * Shows this button
30359      */
30360     show: function(){
30361         this.hidden = false;
30362         this.td.style.display = "";
30363     },
30364     
30365     /**
30366      * Hides this button
30367      */
30368     hide: function(){
30369         this.hidden = true;
30370         this.td.style.display = "none";
30371     }
30372 });
30373
30374 // backwards compat
30375 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30376  * Based on:
30377  * Ext JS Library 1.1.1
30378  * Copyright(c) 2006-2007, Ext JS, LLC.
30379  *
30380  * Originally Released Under LGPL - original licence link has changed is not relivant.
30381  *
30382  * Fork - LGPL
30383  * <script type="text/javascript">
30384  */
30385  
30386 /**
30387  * @class Roo.PagingToolbar
30388  * @extends Roo.Toolbar
30389  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30390  * @constructor
30391  * Create a new PagingToolbar
30392  * @param {Object} config The config object
30393  */
30394 Roo.PagingToolbar = function(el, ds, config)
30395 {
30396     // old args format still supported... - xtype is prefered..
30397     if (typeof(el) == 'object' && el.xtype) {
30398         // created from xtype...
30399         config = el;
30400         ds = el.dataSource;
30401         el = config.container;
30402     }
30403     var items = [];
30404     if (config.items) {
30405         items = config.items;
30406         config.items = [];
30407     }
30408     
30409     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30410     this.ds = ds;
30411     this.cursor = 0;
30412     this.renderButtons(this.el);
30413     this.bind(ds);
30414     
30415     // supprot items array.
30416    
30417     Roo.each(items, function(e) {
30418         this.add(Roo.factory(e));
30419     },this);
30420     
30421 };
30422
30423 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30424     /**
30425      * @cfg {Roo.data.Store} dataSource
30426      * The underlying data store providing the paged data
30427      */
30428     /**
30429      * @cfg {String/HTMLElement/Element} container
30430      * container The id or element that will contain the toolbar
30431      */
30432     /**
30433      * @cfg {Boolean} displayInfo
30434      * True to display the displayMsg (defaults to false)
30435      */
30436     /**
30437      * @cfg {Number} pageSize
30438      * The number of records to display per page (defaults to 20)
30439      */
30440     pageSize: 20,
30441     /**
30442      * @cfg {String} displayMsg
30443      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30444      */
30445     displayMsg : 'Displaying {0} - {1} of {2}',
30446     /**
30447      * @cfg {String} emptyMsg
30448      * The message to display when no records are found (defaults to "No data to display")
30449      */
30450     emptyMsg : 'No data to display',
30451     /**
30452      * Customizable piece of the default paging text (defaults to "Page")
30453      * @type String
30454      */
30455     beforePageText : "Page",
30456     /**
30457      * Customizable piece of the default paging text (defaults to "of %0")
30458      * @type String
30459      */
30460     afterPageText : "of {0}",
30461     /**
30462      * Customizable piece of the default paging text (defaults to "First Page")
30463      * @type String
30464      */
30465     firstText : "First Page",
30466     /**
30467      * Customizable piece of the default paging text (defaults to "Previous Page")
30468      * @type String
30469      */
30470     prevText : "Previous Page",
30471     /**
30472      * Customizable piece of the default paging text (defaults to "Next Page")
30473      * @type String
30474      */
30475     nextText : "Next Page",
30476     /**
30477      * Customizable piece of the default paging text (defaults to "Last Page")
30478      * @type String
30479      */
30480     lastText : "Last Page",
30481     /**
30482      * Customizable piece of the default paging text (defaults to "Refresh")
30483      * @type String
30484      */
30485     refreshText : "Refresh",
30486
30487     // private
30488     renderButtons : function(el){
30489         Roo.PagingToolbar.superclass.render.call(this, el);
30490         this.first = this.addButton({
30491             tooltip: this.firstText,
30492             cls: "x-btn-icon x-grid-page-first",
30493             disabled: true,
30494             handler: this.onClick.createDelegate(this, ["first"])
30495         });
30496         this.prev = this.addButton({
30497             tooltip: this.prevText,
30498             cls: "x-btn-icon x-grid-page-prev",
30499             disabled: true,
30500             handler: this.onClick.createDelegate(this, ["prev"])
30501         });
30502         //this.addSeparator();
30503         this.add(this.beforePageText);
30504         this.field = Roo.get(this.addDom({
30505            tag: "input",
30506            type: "text",
30507            size: "3",
30508            value: "1",
30509            cls: "x-grid-page-number"
30510         }).el);
30511         this.field.on("keydown", this.onPagingKeydown, this);
30512         this.field.on("focus", function(){this.dom.select();});
30513         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30514         this.field.setHeight(18);
30515         //this.addSeparator();
30516         this.next = this.addButton({
30517             tooltip: this.nextText,
30518             cls: "x-btn-icon x-grid-page-next",
30519             disabled: true,
30520             handler: this.onClick.createDelegate(this, ["next"])
30521         });
30522         this.last = this.addButton({
30523             tooltip: this.lastText,
30524             cls: "x-btn-icon x-grid-page-last",
30525             disabled: true,
30526             handler: this.onClick.createDelegate(this, ["last"])
30527         });
30528         //this.addSeparator();
30529         this.loading = this.addButton({
30530             tooltip: this.refreshText,
30531             cls: "x-btn-icon x-grid-loading",
30532             handler: this.onClick.createDelegate(this, ["refresh"])
30533         });
30534
30535         if(this.displayInfo){
30536             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30537         }
30538     },
30539
30540     // private
30541     updateInfo : function(){
30542         if(this.displayEl){
30543             var count = this.ds.getCount();
30544             var msg = count == 0 ?
30545                 this.emptyMsg :
30546                 String.format(
30547                     this.displayMsg,
30548                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30549                 );
30550             this.displayEl.update(msg);
30551         }
30552     },
30553
30554     // private
30555     onLoad : function(ds, r, o){
30556        this.cursor = o.params ? o.params.start : 0;
30557        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30558
30559        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30560        this.field.dom.value = ap;
30561        this.first.setDisabled(ap == 1);
30562        this.prev.setDisabled(ap == 1);
30563        this.next.setDisabled(ap == ps);
30564        this.last.setDisabled(ap == ps);
30565        this.loading.enable();
30566        this.updateInfo();
30567     },
30568
30569     // private
30570     getPageData : function(){
30571         var total = this.ds.getTotalCount();
30572         return {
30573             total : total,
30574             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30575             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30576         };
30577     },
30578
30579     // private
30580     onLoadError : function(){
30581         this.loading.enable();
30582     },
30583
30584     // private
30585     onPagingKeydown : function(e){
30586         var k = e.getKey();
30587         var d = this.getPageData();
30588         if(k == e.RETURN){
30589             var v = this.field.dom.value, pageNum;
30590             if(!v || isNaN(pageNum = parseInt(v, 10))){
30591                 this.field.dom.value = d.activePage;
30592                 return;
30593             }
30594             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30595             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30596             e.stopEvent();
30597         }
30598         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))
30599         {
30600           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30601           this.field.dom.value = pageNum;
30602           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30603           e.stopEvent();
30604         }
30605         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30606         {
30607           var v = this.field.dom.value, pageNum; 
30608           var increment = (e.shiftKey) ? 10 : 1;
30609           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30610             increment *= -1;
30611           }
30612           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30613             this.field.dom.value = d.activePage;
30614             return;
30615           }
30616           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30617           {
30618             this.field.dom.value = parseInt(v, 10) + increment;
30619             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30620             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30621           }
30622           e.stopEvent();
30623         }
30624     },
30625
30626     // private
30627     beforeLoad : function(){
30628         if(this.loading){
30629             this.loading.disable();
30630         }
30631     },
30632
30633     // private
30634     onClick : function(which){
30635         var ds = this.ds;
30636         switch(which){
30637             case "first":
30638                 ds.load({params:{start: 0, limit: this.pageSize}});
30639             break;
30640             case "prev":
30641                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30642             break;
30643             case "next":
30644                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30645             break;
30646             case "last":
30647                 var total = ds.getTotalCount();
30648                 var extra = total % this.pageSize;
30649                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30650                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30651             break;
30652             case "refresh":
30653                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30654             break;
30655         }
30656     },
30657
30658     /**
30659      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30660      * @param {Roo.data.Store} store The data store to unbind
30661      */
30662     unbind : function(ds){
30663         ds.un("beforeload", this.beforeLoad, this);
30664         ds.un("load", this.onLoad, this);
30665         ds.un("loadexception", this.onLoadError, this);
30666         ds.un("remove", this.updateInfo, this);
30667         ds.un("add", this.updateInfo, this);
30668         this.ds = undefined;
30669     },
30670
30671     /**
30672      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30673      * @param {Roo.data.Store} store The data store to bind
30674      */
30675     bind : function(ds){
30676         ds.on("beforeload", this.beforeLoad, this);
30677         ds.on("load", this.onLoad, this);
30678         ds.on("loadexception", this.onLoadError, this);
30679         ds.on("remove", this.updateInfo, this);
30680         ds.on("add", this.updateInfo, this);
30681         this.ds = ds;
30682     }
30683 });/*
30684  * Based on:
30685  * Ext JS Library 1.1.1
30686  * Copyright(c) 2006-2007, Ext JS, LLC.
30687  *
30688  * Originally Released Under LGPL - original licence link has changed is not relivant.
30689  *
30690  * Fork - LGPL
30691  * <script type="text/javascript">
30692  */
30693
30694 /**
30695  * @class Roo.Resizable
30696  * @extends Roo.util.Observable
30697  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30698  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30699  * 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
30700  * the element will be wrapped for you automatically.</p>
30701  * <p>Here is the list of valid resize handles:</p>
30702  * <pre>
30703 Value   Description
30704 ------  -------------------
30705  'n'     north
30706  's'     south
30707  'e'     east
30708  'w'     west
30709  'nw'    northwest
30710  'sw'    southwest
30711  'se'    southeast
30712  'ne'    northeast
30713  'hd'    horizontal drag
30714  'all'   all
30715 </pre>
30716  * <p>Here's an example showing the creation of a typical Resizable:</p>
30717  * <pre><code>
30718 var resizer = new Roo.Resizable("element-id", {
30719     handles: 'all',
30720     minWidth: 200,
30721     minHeight: 100,
30722     maxWidth: 500,
30723     maxHeight: 400,
30724     pinned: true
30725 });
30726 resizer.on("resize", myHandler);
30727 </code></pre>
30728  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30729  * resizer.east.setDisplayed(false);</p>
30730  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30731  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30732  * resize operation's new size (defaults to [0, 0])
30733  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30734  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30735  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30736  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30737  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30738  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30739  * @cfg {Number} width The width of the element in pixels (defaults to null)
30740  * @cfg {Number} height The height of the element in pixels (defaults to null)
30741  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30742  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30743  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30744  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30745  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30746  * in favor of the handles config option (defaults to false)
30747  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30748  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30749  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30750  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30751  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30752  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30753  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30754  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30755  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30756  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30757  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30758  * @constructor
30759  * Create a new resizable component
30760  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30761  * @param {Object} config configuration options
30762   */
30763 Roo.Resizable = function(el, config)
30764 {
30765     this.el = Roo.get(el);
30766
30767     if(config && config.wrap){
30768         config.resizeChild = this.el;
30769         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30770         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30771         this.el.setStyle("overflow", "hidden");
30772         this.el.setPositioning(config.resizeChild.getPositioning());
30773         config.resizeChild.clearPositioning();
30774         if(!config.width || !config.height){
30775             var csize = config.resizeChild.getSize();
30776             this.el.setSize(csize.width, csize.height);
30777         }
30778         if(config.pinned && !config.adjustments){
30779             config.adjustments = "auto";
30780         }
30781     }
30782
30783     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30784     this.proxy.unselectable();
30785     this.proxy.enableDisplayMode('block');
30786
30787     Roo.apply(this, config);
30788
30789     if(this.pinned){
30790         this.disableTrackOver = true;
30791         this.el.addClass("x-resizable-pinned");
30792     }
30793     // if the element isn't positioned, make it relative
30794     var position = this.el.getStyle("position");
30795     if(position != "absolute" && position != "fixed"){
30796         this.el.setStyle("position", "relative");
30797     }
30798     if(!this.handles){ // no handles passed, must be legacy style
30799         this.handles = 's,e,se';
30800         if(this.multiDirectional){
30801             this.handles += ',n,w';
30802         }
30803     }
30804     if(this.handles == "all"){
30805         this.handles = "n s e w ne nw se sw";
30806     }
30807     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30808     var ps = Roo.Resizable.positions;
30809     for(var i = 0, len = hs.length; i < len; i++){
30810         if(hs[i] && ps[hs[i]]){
30811             var pos = ps[hs[i]];
30812             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30813         }
30814     }
30815     // legacy
30816     this.corner = this.southeast;
30817     
30818     // updateBox = the box can move..
30819     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30820         this.updateBox = true;
30821     }
30822
30823     this.activeHandle = null;
30824
30825     if(this.resizeChild){
30826         if(typeof this.resizeChild == "boolean"){
30827             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30828         }else{
30829             this.resizeChild = Roo.get(this.resizeChild, true);
30830         }
30831     }
30832     
30833     if(this.adjustments == "auto"){
30834         var rc = this.resizeChild;
30835         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30836         if(rc && (hw || hn)){
30837             rc.position("relative");
30838             rc.setLeft(hw ? hw.el.getWidth() : 0);
30839             rc.setTop(hn ? hn.el.getHeight() : 0);
30840         }
30841         this.adjustments = [
30842             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30843             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30844         ];
30845     }
30846
30847     if(this.draggable){
30848         this.dd = this.dynamic ?
30849             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30850         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30851     }
30852
30853     // public events
30854     this.addEvents({
30855         /**
30856          * @event beforeresize
30857          * Fired before resize is allowed. Set enabled to false to cancel resize.
30858          * @param {Roo.Resizable} this
30859          * @param {Roo.EventObject} e The mousedown event
30860          */
30861         "beforeresize" : true,
30862         /**
30863          * @event resizing
30864          * Fired a resizing.
30865          * @param {Roo.Resizable} this
30866          * @param {Number} x The new x position
30867          * @param {Number} y The new y position
30868          * @param {Number} w The new w width
30869          * @param {Number} h The new h hight
30870          * @param {Roo.EventObject} e The mouseup event
30871          */
30872         "resizing" : true,
30873         /**
30874          * @event resize
30875          * Fired after a resize.
30876          * @param {Roo.Resizable} this
30877          * @param {Number} width The new width
30878          * @param {Number} height The new height
30879          * @param {Roo.EventObject} e The mouseup event
30880          */
30881         "resize" : true
30882     });
30883
30884     if(this.width !== null && this.height !== null){
30885         this.resizeTo(this.width, this.height);
30886     }else{
30887         this.updateChildSize();
30888     }
30889     if(Roo.isIE){
30890         this.el.dom.style.zoom = 1;
30891     }
30892     Roo.Resizable.superclass.constructor.call(this);
30893 };
30894
30895 Roo.extend(Roo.Resizable, Roo.util.Observable, {
30896         resizeChild : false,
30897         adjustments : [0, 0],
30898         minWidth : 5,
30899         minHeight : 5,
30900         maxWidth : 10000,
30901         maxHeight : 10000,
30902         enabled : true,
30903         animate : false,
30904         duration : .35,
30905         dynamic : false,
30906         handles : false,
30907         multiDirectional : false,
30908         disableTrackOver : false,
30909         easing : 'easeOutStrong',
30910         widthIncrement : 0,
30911         heightIncrement : 0,
30912         pinned : false,
30913         width : null,
30914         height : null,
30915         preserveRatio : false,
30916         transparent: false,
30917         minX: 0,
30918         minY: 0,
30919         draggable: false,
30920
30921         /**
30922          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
30923          */
30924         constrainTo: undefined,
30925         /**
30926          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
30927          */
30928         resizeRegion: undefined,
30929
30930
30931     /**
30932      * Perform a manual resize
30933      * @param {Number} width
30934      * @param {Number} height
30935      */
30936     resizeTo : function(width, height){
30937         this.el.setSize(width, height);
30938         this.updateChildSize();
30939         this.fireEvent("resize", this, width, height, null);
30940     },
30941
30942     // private
30943     startSizing : function(e, handle){
30944         this.fireEvent("beforeresize", this, e);
30945         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
30946
30947             if(!this.overlay){
30948                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
30949                 this.overlay.unselectable();
30950                 this.overlay.enableDisplayMode("block");
30951                 this.overlay.on("mousemove", this.onMouseMove, this);
30952                 this.overlay.on("mouseup", this.onMouseUp, this);
30953             }
30954             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
30955
30956             this.resizing = true;
30957             this.startBox = this.el.getBox();
30958             this.startPoint = e.getXY();
30959             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
30960                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
30961
30962             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30963             this.overlay.show();
30964
30965             if(this.constrainTo) {
30966                 var ct = Roo.get(this.constrainTo);
30967                 this.resizeRegion = ct.getRegion().adjust(
30968                     ct.getFrameWidth('t'),
30969                     ct.getFrameWidth('l'),
30970                     -ct.getFrameWidth('b'),
30971                     -ct.getFrameWidth('r')
30972                 );
30973             }
30974
30975             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
30976             this.proxy.show();
30977             this.proxy.setBox(this.startBox);
30978             if(!this.dynamic){
30979                 this.proxy.setStyle('visibility', 'visible');
30980             }
30981         }
30982     },
30983
30984     // private
30985     onMouseDown : function(handle, e){
30986         if(this.enabled){
30987             e.stopEvent();
30988             this.activeHandle = handle;
30989             this.startSizing(e, handle);
30990         }
30991     },
30992
30993     // private
30994     onMouseUp : function(e){
30995         var size = this.resizeElement();
30996         this.resizing = false;
30997         this.handleOut();
30998         this.overlay.hide();
30999         this.proxy.hide();
31000         this.fireEvent("resize", this, size.width, size.height, e);
31001     },
31002
31003     // private
31004     updateChildSize : function(){
31005         
31006         if(this.resizeChild){
31007             var el = this.el;
31008             var child = this.resizeChild;
31009             var adj = this.adjustments;
31010             if(el.dom.offsetWidth){
31011                 var b = el.getSize(true);
31012                 child.setSize(b.width+adj[0], b.height+adj[1]);
31013             }
31014             // Second call here for IE
31015             // The first call enables instant resizing and
31016             // the second call corrects scroll bars if they
31017             // exist
31018             if(Roo.isIE){
31019                 setTimeout(function(){
31020                     if(el.dom.offsetWidth){
31021                         var b = el.getSize(true);
31022                         child.setSize(b.width+adj[0], b.height+adj[1]);
31023                     }
31024                 }, 10);
31025             }
31026         }
31027     },
31028
31029     // private
31030     snap : function(value, inc, min){
31031         if(!inc || !value) {
31032             return value;
31033         }
31034         var newValue = value;
31035         var m = value % inc;
31036         if(m > 0){
31037             if(m > (inc/2)){
31038                 newValue = value + (inc-m);
31039             }else{
31040                 newValue = value - m;
31041             }
31042         }
31043         return Math.max(min, newValue);
31044     },
31045
31046     // private
31047     resizeElement : function(){
31048         var box = this.proxy.getBox();
31049         if(this.updateBox){
31050             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31051         }else{
31052             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31053         }
31054         this.updateChildSize();
31055         if(!this.dynamic){
31056             this.proxy.hide();
31057         }
31058         return box;
31059     },
31060
31061     // private
31062     constrain : function(v, diff, m, mx){
31063         if(v - diff < m){
31064             diff = v - m;
31065         }else if(v - diff > mx){
31066             diff = mx - v;
31067         }
31068         return diff;
31069     },
31070
31071     // private
31072     onMouseMove : function(e){
31073         
31074         if(this.enabled){
31075             try{// try catch so if something goes wrong the user doesn't get hung
31076
31077             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31078                 return;
31079             }
31080
31081             //var curXY = this.startPoint;
31082             var curSize = this.curSize || this.startBox;
31083             var x = this.startBox.x, y = this.startBox.y;
31084             var ox = x, oy = y;
31085             var w = curSize.width, h = curSize.height;
31086             var ow = w, oh = h;
31087             var mw = this.minWidth, mh = this.minHeight;
31088             var mxw = this.maxWidth, mxh = this.maxHeight;
31089             var wi = this.widthIncrement;
31090             var hi = this.heightIncrement;
31091
31092             var eventXY = e.getXY();
31093             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31094             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31095
31096             var pos = this.activeHandle.position;
31097
31098             switch(pos){
31099                 case "east":
31100                     w += diffX;
31101                     w = Math.min(Math.max(mw, w), mxw);
31102                     break;
31103              
31104                 case "south":
31105                     h += diffY;
31106                     h = Math.min(Math.max(mh, h), mxh);
31107                     break;
31108                 case "southeast":
31109                     w += diffX;
31110                     h += diffY;
31111                     w = Math.min(Math.max(mw, w), mxw);
31112                     h = Math.min(Math.max(mh, h), mxh);
31113                     break;
31114                 case "north":
31115                     diffY = this.constrain(h, diffY, mh, mxh);
31116                     y += diffY;
31117                     h -= diffY;
31118                     break;
31119                 case "hdrag":
31120                     
31121                     if (wi) {
31122                         var adiffX = Math.abs(diffX);
31123                         var sub = (adiffX % wi); // how much 
31124                         if (sub > (wi/2)) { // far enough to snap
31125                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31126                         } else {
31127                             // remove difference.. 
31128                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31129                         }
31130                     }
31131                     x += diffX;
31132                     x = Math.max(this.minX, x);
31133                     break;
31134                 case "west":
31135                     diffX = this.constrain(w, diffX, mw, mxw);
31136                     x += diffX;
31137                     w -= diffX;
31138                     break;
31139                 case "northeast":
31140                     w += diffX;
31141                     w = Math.min(Math.max(mw, w), mxw);
31142                     diffY = this.constrain(h, diffY, mh, mxh);
31143                     y += diffY;
31144                     h -= diffY;
31145                     break;
31146                 case "northwest":
31147                     diffX = this.constrain(w, diffX, mw, mxw);
31148                     diffY = this.constrain(h, diffY, mh, mxh);
31149                     y += diffY;
31150                     h -= diffY;
31151                     x += diffX;
31152                     w -= diffX;
31153                     break;
31154                case "southwest":
31155                     diffX = this.constrain(w, diffX, mw, mxw);
31156                     h += diffY;
31157                     h = Math.min(Math.max(mh, h), mxh);
31158                     x += diffX;
31159                     w -= diffX;
31160                     break;
31161             }
31162
31163             var sw = this.snap(w, wi, mw);
31164             var sh = this.snap(h, hi, mh);
31165             if(sw != w || sh != h){
31166                 switch(pos){
31167                     case "northeast":
31168                         y -= sh - h;
31169                     break;
31170                     case "north":
31171                         y -= sh - h;
31172                         break;
31173                     case "southwest":
31174                         x -= sw - w;
31175                     break;
31176                     case "west":
31177                         x -= sw - w;
31178                         break;
31179                     case "northwest":
31180                         x -= sw - w;
31181                         y -= sh - h;
31182                     break;
31183                 }
31184                 w = sw;
31185                 h = sh;
31186             }
31187
31188             if(this.preserveRatio){
31189                 switch(pos){
31190                     case "southeast":
31191                     case "east":
31192                         h = oh * (w/ow);
31193                         h = Math.min(Math.max(mh, h), mxh);
31194                         w = ow * (h/oh);
31195                        break;
31196                     case "south":
31197                         w = ow * (h/oh);
31198                         w = Math.min(Math.max(mw, w), mxw);
31199                         h = oh * (w/ow);
31200                         break;
31201                     case "northeast":
31202                         w = ow * (h/oh);
31203                         w = Math.min(Math.max(mw, w), mxw);
31204                         h = oh * (w/ow);
31205                     break;
31206                     case "north":
31207                         var tw = w;
31208                         w = ow * (h/oh);
31209                         w = Math.min(Math.max(mw, w), mxw);
31210                         h = oh * (w/ow);
31211                         x += (tw - w) / 2;
31212                         break;
31213                     case "southwest":
31214                         h = oh * (w/ow);
31215                         h = Math.min(Math.max(mh, h), mxh);
31216                         var tw = w;
31217                         w = ow * (h/oh);
31218                         x += tw - w;
31219                         break;
31220                     case "west":
31221                         var th = h;
31222                         h = oh * (w/ow);
31223                         h = Math.min(Math.max(mh, h), mxh);
31224                         y += (th - h) / 2;
31225                         var tw = w;
31226                         w = ow * (h/oh);
31227                         x += tw - w;
31228                        break;
31229                     case "northwest":
31230                         var tw = w;
31231                         var th = h;
31232                         h = oh * (w/ow);
31233                         h = Math.min(Math.max(mh, h), mxh);
31234                         w = ow * (h/oh);
31235                         y += th - h;
31236                         x += tw - w;
31237                        break;
31238
31239                 }
31240             }
31241             if (pos == 'hdrag') {
31242                 w = ow;
31243             }
31244             this.proxy.setBounds(x, y, w, h);
31245             if(this.dynamic){
31246                 this.resizeElement();
31247             }
31248             }catch(e){}
31249         }
31250         this.fireEvent("resizing", this, x, y, w, h, e);
31251     },
31252
31253     // private
31254     handleOver : function(){
31255         if(this.enabled){
31256             this.el.addClass("x-resizable-over");
31257         }
31258     },
31259
31260     // private
31261     handleOut : function(){
31262         if(!this.resizing){
31263             this.el.removeClass("x-resizable-over");
31264         }
31265     },
31266
31267     /**
31268      * Returns the element this component is bound to.
31269      * @return {Roo.Element}
31270      */
31271     getEl : function(){
31272         return this.el;
31273     },
31274
31275     /**
31276      * Returns the resizeChild element (or null).
31277      * @return {Roo.Element}
31278      */
31279     getResizeChild : function(){
31280         return this.resizeChild;
31281     },
31282     groupHandler : function()
31283     {
31284         
31285     },
31286     /**
31287      * Destroys this resizable. If the element was wrapped and
31288      * removeEl is not true then the element remains.
31289      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31290      */
31291     destroy : function(removeEl){
31292         this.proxy.remove();
31293         if(this.overlay){
31294             this.overlay.removeAllListeners();
31295             this.overlay.remove();
31296         }
31297         var ps = Roo.Resizable.positions;
31298         for(var k in ps){
31299             if(typeof ps[k] != "function" && this[ps[k]]){
31300                 var h = this[ps[k]];
31301                 h.el.removeAllListeners();
31302                 h.el.remove();
31303             }
31304         }
31305         if(removeEl){
31306             this.el.update("");
31307             this.el.remove();
31308         }
31309     }
31310 });
31311
31312 // private
31313 // hash to map config positions to true positions
31314 Roo.Resizable.positions = {
31315     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31316     hd: "hdrag"
31317 };
31318
31319 // private
31320 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31321     if(!this.tpl){
31322         // only initialize the template if resizable is used
31323         var tpl = Roo.DomHelper.createTemplate(
31324             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31325         );
31326         tpl.compile();
31327         Roo.Resizable.Handle.prototype.tpl = tpl;
31328     }
31329     this.position = pos;
31330     this.rz = rz;
31331     // show north drag fro topdra
31332     var handlepos = pos == 'hdrag' ? 'north' : pos;
31333     
31334     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31335     if (pos == 'hdrag') {
31336         this.el.setStyle('cursor', 'pointer');
31337     }
31338     this.el.unselectable();
31339     if(transparent){
31340         this.el.setOpacity(0);
31341     }
31342     this.el.on("mousedown", this.onMouseDown, this);
31343     if(!disableTrackOver){
31344         this.el.on("mouseover", this.onMouseOver, this);
31345         this.el.on("mouseout", this.onMouseOut, this);
31346     }
31347 };
31348
31349 // private
31350 Roo.Resizable.Handle.prototype = {
31351     afterResize : function(rz){
31352         Roo.log('after?');
31353         // do nothing
31354     },
31355     // private
31356     onMouseDown : function(e){
31357         this.rz.onMouseDown(this, e);
31358     },
31359     // private
31360     onMouseOver : function(e){
31361         this.rz.handleOver(this, e);
31362     },
31363     // private
31364     onMouseOut : function(e){
31365         this.rz.handleOut(this, e);
31366     }
31367 };/*
31368  * Based on:
31369  * Ext JS Library 1.1.1
31370  * Copyright(c) 2006-2007, Ext JS, LLC.
31371  *
31372  * Originally Released Under LGPL - original licence link has changed is not relivant.
31373  *
31374  * Fork - LGPL
31375  * <script type="text/javascript">
31376  */
31377
31378 /**
31379  * @class Roo.Editor
31380  * @extends Roo.Component
31381  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31382  * @constructor
31383  * Create a new Editor
31384  * @param {Roo.form.Field} field The Field object (or descendant)
31385  * @param {Object} config The config object
31386  */
31387 Roo.Editor = function(field, config){
31388     Roo.Editor.superclass.constructor.call(this, config);
31389     this.field = field;
31390     this.addEvents({
31391         /**
31392              * @event beforestartedit
31393              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31394              * false from the handler of this event.
31395              * @param {Editor} this
31396              * @param {Roo.Element} boundEl The underlying element bound to this editor
31397              * @param {Mixed} value The field value being set
31398              */
31399         "beforestartedit" : true,
31400         /**
31401              * @event startedit
31402              * Fires when this editor is displayed
31403              * @param {Roo.Element} boundEl The underlying element bound to this editor
31404              * @param {Mixed} value The starting field value
31405              */
31406         "startedit" : true,
31407         /**
31408              * @event beforecomplete
31409              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31410              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31411              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31412              * event will not fire since no edit actually occurred.
31413              * @param {Editor} this
31414              * @param {Mixed} value The current field value
31415              * @param {Mixed} startValue The original field value
31416              */
31417         "beforecomplete" : true,
31418         /**
31419              * @event complete
31420              * Fires after editing is complete and any changed value has been written to the underlying field.
31421              * @param {Editor} this
31422              * @param {Mixed} value The current field value
31423              * @param {Mixed} startValue The original field value
31424              */
31425         "complete" : true,
31426         /**
31427          * @event specialkey
31428          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31429          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31430          * @param {Roo.form.Field} this
31431          * @param {Roo.EventObject} e The event object
31432          */
31433         "specialkey" : true
31434     });
31435 };
31436
31437 Roo.extend(Roo.Editor, Roo.Component, {
31438     /**
31439      * @cfg {Boolean/String} autosize
31440      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31441      * or "height" to adopt the height only (defaults to false)
31442      */
31443     /**
31444      * @cfg {Boolean} revertInvalid
31445      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31446      * validation fails (defaults to true)
31447      */
31448     /**
31449      * @cfg {Boolean} ignoreNoChange
31450      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31451      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31452      * will never be ignored.
31453      */
31454     /**
31455      * @cfg {Boolean} hideEl
31456      * False to keep the bound element visible while the editor is displayed (defaults to true)
31457      */
31458     /**
31459      * @cfg {Mixed} value
31460      * The data value of the underlying field (defaults to "")
31461      */
31462     value : "",
31463     /**
31464      * @cfg {String} alignment
31465      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31466      */
31467     alignment: "c-c?",
31468     /**
31469      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31470      * for bottom-right shadow (defaults to "frame")
31471      */
31472     shadow : "frame",
31473     /**
31474      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31475      */
31476     constrain : false,
31477     /**
31478      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31479      */
31480     completeOnEnter : false,
31481     /**
31482      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31483      */
31484     cancelOnEsc : false,
31485     /**
31486      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31487      */
31488     updateEl : false,
31489
31490     // private
31491     onRender : function(ct, position){
31492         this.el = new Roo.Layer({
31493             shadow: this.shadow,
31494             cls: "x-editor",
31495             parentEl : ct,
31496             shim : this.shim,
31497             shadowOffset:4,
31498             id: this.id,
31499             constrain: this.constrain
31500         });
31501         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31502         if(this.field.msgTarget != 'title'){
31503             this.field.msgTarget = 'qtip';
31504         }
31505         this.field.render(this.el);
31506         if(Roo.isGecko){
31507             this.field.el.dom.setAttribute('autocomplete', 'off');
31508         }
31509         this.field.on("specialkey", this.onSpecialKey, this);
31510         if(this.swallowKeys){
31511             this.field.el.swallowEvent(['keydown','keypress']);
31512         }
31513         this.field.show();
31514         this.field.on("blur", this.onBlur, this);
31515         if(this.field.grow){
31516             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31517         }
31518     },
31519
31520     onSpecialKey : function(field, e)
31521     {
31522         //Roo.log('editor onSpecialKey');
31523         if(this.completeOnEnter && e.getKey() == e.ENTER){
31524             e.stopEvent();
31525             this.completeEdit();
31526             return;
31527         }
31528         // do not fire special key otherwise it might hide close the editor...
31529         if(e.getKey() == e.ENTER){    
31530             return;
31531         }
31532         if(this.cancelOnEsc && e.getKey() == e.ESC){
31533             this.cancelEdit();
31534             return;
31535         } 
31536         this.fireEvent('specialkey', field, e);
31537     
31538     },
31539
31540     /**
31541      * Starts the editing process and shows the editor.
31542      * @param {String/HTMLElement/Element} el The element to edit
31543      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31544       * to the innerHTML of el.
31545      */
31546     startEdit : function(el, value){
31547         if(this.editing){
31548             this.completeEdit();
31549         }
31550         this.boundEl = Roo.get(el);
31551         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31552         if(!this.rendered){
31553             this.render(this.parentEl || document.body);
31554         }
31555         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31556             return;
31557         }
31558         this.startValue = v;
31559         this.field.setValue(v);
31560         if(this.autoSize){
31561             var sz = this.boundEl.getSize();
31562             switch(this.autoSize){
31563                 case "width":
31564                 this.setSize(sz.width,  "");
31565                 break;
31566                 case "height":
31567                 this.setSize("",  sz.height);
31568                 break;
31569                 default:
31570                 this.setSize(sz.width,  sz.height);
31571             }
31572         }
31573         this.el.alignTo(this.boundEl, this.alignment);
31574         this.editing = true;
31575         if(Roo.QuickTips){
31576             Roo.QuickTips.disable();
31577         }
31578         this.show();
31579     },
31580
31581     /**
31582      * Sets the height and width of this editor.
31583      * @param {Number} width The new width
31584      * @param {Number} height The new height
31585      */
31586     setSize : function(w, h){
31587         this.field.setSize(w, h);
31588         if(this.el){
31589             this.el.sync();
31590         }
31591     },
31592
31593     /**
31594      * Realigns the editor to the bound field based on the current alignment config value.
31595      */
31596     realign : function(){
31597         this.el.alignTo(this.boundEl, this.alignment);
31598     },
31599
31600     /**
31601      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31602      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31603      */
31604     completeEdit : function(remainVisible){
31605         if(!this.editing){
31606             return;
31607         }
31608         var v = this.getValue();
31609         if(this.revertInvalid !== false && !this.field.isValid()){
31610             v = this.startValue;
31611             this.cancelEdit(true);
31612         }
31613         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31614             this.editing = false;
31615             this.hide();
31616             return;
31617         }
31618         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31619             this.editing = false;
31620             if(this.updateEl && this.boundEl){
31621                 this.boundEl.update(v);
31622             }
31623             if(remainVisible !== true){
31624                 this.hide();
31625             }
31626             this.fireEvent("complete", this, v, this.startValue);
31627         }
31628     },
31629
31630     // private
31631     onShow : function(){
31632         this.el.show();
31633         if(this.hideEl !== false){
31634             this.boundEl.hide();
31635         }
31636         this.field.show();
31637         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31638             this.fixIEFocus = true;
31639             this.deferredFocus.defer(50, this);
31640         }else{
31641             this.field.focus();
31642         }
31643         this.fireEvent("startedit", this.boundEl, this.startValue);
31644     },
31645
31646     deferredFocus : function(){
31647         if(this.editing){
31648             this.field.focus();
31649         }
31650     },
31651
31652     /**
31653      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31654      * reverted to the original starting value.
31655      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31656      * cancel (defaults to false)
31657      */
31658     cancelEdit : function(remainVisible){
31659         if(this.editing){
31660             this.setValue(this.startValue);
31661             if(remainVisible !== true){
31662                 this.hide();
31663             }
31664         }
31665     },
31666
31667     // private
31668     onBlur : function(){
31669         if(this.allowBlur !== true && this.editing){
31670             this.completeEdit();
31671         }
31672     },
31673
31674     // private
31675     onHide : function(){
31676         if(this.editing){
31677             this.completeEdit();
31678             return;
31679         }
31680         this.field.blur();
31681         if(this.field.collapse){
31682             this.field.collapse();
31683         }
31684         this.el.hide();
31685         if(this.hideEl !== false){
31686             this.boundEl.show();
31687         }
31688         if(Roo.QuickTips){
31689             Roo.QuickTips.enable();
31690         }
31691     },
31692
31693     /**
31694      * Sets the data value of the editor
31695      * @param {Mixed} value Any valid value supported by the underlying field
31696      */
31697     setValue : function(v){
31698         this.field.setValue(v);
31699     },
31700
31701     /**
31702      * Gets the data value of the editor
31703      * @return {Mixed} The data value
31704      */
31705     getValue : function(){
31706         return this.field.getValue();
31707     }
31708 });/*
31709  * Based on:
31710  * Ext JS Library 1.1.1
31711  * Copyright(c) 2006-2007, Ext JS, LLC.
31712  *
31713  * Originally Released Under LGPL - original licence link has changed is not relivant.
31714  *
31715  * Fork - LGPL
31716  * <script type="text/javascript">
31717  */
31718  
31719 /**
31720  * @class Roo.BasicDialog
31721  * @extends Roo.util.Observable
31722  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31723  * <pre><code>
31724 var dlg = new Roo.BasicDialog("my-dlg", {
31725     height: 200,
31726     width: 300,
31727     minHeight: 100,
31728     minWidth: 150,
31729     modal: true,
31730     proxyDrag: true,
31731     shadow: true
31732 });
31733 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31734 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31735 dlg.addButton('Cancel', dlg.hide, dlg);
31736 dlg.show();
31737 </code></pre>
31738   <b>A Dialog should always be a direct child of the body element.</b>
31739  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31740  * @cfg {String} title Default text to display in the title bar (defaults to null)
31741  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31742  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31743  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31744  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31745  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31746  * (defaults to null with no animation)
31747  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31748  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31749  * property for valid values (defaults to 'all')
31750  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31751  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31752  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31753  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31754  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31755  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31756  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31757  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31758  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31759  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31760  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31761  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31762  * draggable = true (defaults to false)
31763  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31764  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31765  * shadow (defaults to false)
31766  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31767  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31768  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31769  * @cfg {Array} buttons Array of buttons
31770  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31771  * @constructor
31772  * Create a new BasicDialog.
31773  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31774  * @param {Object} config Configuration options
31775  */
31776 Roo.BasicDialog = function(el, config){
31777     this.el = Roo.get(el);
31778     var dh = Roo.DomHelper;
31779     if(!this.el && config && config.autoCreate){
31780         if(typeof config.autoCreate == "object"){
31781             if(!config.autoCreate.id){
31782                 config.autoCreate.id = el;
31783             }
31784             this.el = dh.append(document.body,
31785                         config.autoCreate, true);
31786         }else{
31787             this.el = dh.append(document.body,
31788                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31789         }
31790     }
31791     el = this.el;
31792     el.setDisplayed(true);
31793     el.hide = this.hideAction;
31794     this.id = el.id;
31795     el.addClass("x-dlg");
31796
31797     Roo.apply(this, config);
31798
31799     this.proxy = el.createProxy("x-dlg-proxy");
31800     this.proxy.hide = this.hideAction;
31801     this.proxy.setOpacity(.5);
31802     this.proxy.hide();
31803
31804     if(config.width){
31805         el.setWidth(config.width);
31806     }
31807     if(config.height){
31808         el.setHeight(config.height);
31809     }
31810     this.size = el.getSize();
31811     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31812         this.xy = [config.x,config.y];
31813     }else{
31814         this.xy = el.getCenterXY(true);
31815     }
31816     /** The header element @type Roo.Element */
31817     this.header = el.child("> .x-dlg-hd");
31818     /** The body element @type Roo.Element */
31819     this.body = el.child("> .x-dlg-bd");
31820     /** The footer element @type Roo.Element */
31821     this.footer = el.child("> .x-dlg-ft");
31822
31823     if(!this.header){
31824         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31825     }
31826     if(!this.body){
31827         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31828     }
31829
31830     this.header.unselectable();
31831     if(this.title){
31832         this.header.update(this.title);
31833     }
31834     // this element allows the dialog to be focused for keyboard event
31835     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31836     this.focusEl.swallowEvent("click", true);
31837
31838     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31839
31840     // wrap the body and footer for special rendering
31841     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31842     if(this.footer){
31843         this.bwrap.dom.appendChild(this.footer.dom);
31844     }
31845
31846     this.bg = this.el.createChild({
31847         tag: "div", cls:"x-dlg-bg",
31848         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31849     });
31850     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31851
31852
31853     if(this.autoScroll !== false && !this.autoTabs){
31854         this.body.setStyle("overflow", "auto");
31855     }
31856
31857     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31858
31859     if(this.closable !== false){
31860         this.el.addClass("x-dlg-closable");
31861         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31862         this.close.on("click", this.closeClick, this);
31863         this.close.addClassOnOver("x-dlg-close-over");
31864     }
31865     if(this.collapsible !== false){
31866         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31867         this.collapseBtn.on("click", this.collapseClick, this);
31868         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31869         this.header.on("dblclick", this.collapseClick, this);
31870     }
31871     if(this.resizable !== false){
31872         this.el.addClass("x-dlg-resizable");
31873         this.resizer = new Roo.Resizable(el, {
31874             minWidth: this.minWidth || 80,
31875             minHeight:this.minHeight || 80,
31876             handles: this.resizeHandles || "all",
31877             pinned: true
31878         });
31879         this.resizer.on("beforeresize", this.beforeResize, this);
31880         this.resizer.on("resize", this.onResize, this);
31881     }
31882     if(this.draggable !== false){
31883         el.addClass("x-dlg-draggable");
31884         if (!this.proxyDrag) {
31885             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31886         }
31887         else {
31888             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
31889         }
31890         dd.setHandleElId(this.header.id);
31891         dd.endDrag = this.endMove.createDelegate(this);
31892         dd.startDrag = this.startMove.createDelegate(this);
31893         dd.onDrag = this.onDrag.createDelegate(this);
31894         dd.scroll = false;
31895         this.dd = dd;
31896     }
31897     if(this.modal){
31898         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
31899         this.mask.enableDisplayMode("block");
31900         this.mask.hide();
31901         this.el.addClass("x-dlg-modal");
31902     }
31903     if(this.shadow){
31904         this.shadow = new Roo.Shadow({
31905             mode : typeof this.shadow == "string" ? this.shadow : "sides",
31906             offset : this.shadowOffset
31907         });
31908     }else{
31909         this.shadowOffset = 0;
31910     }
31911     if(Roo.useShims && this.shim !== false){
31912         this.shim = this.el.createShim();
31913         this.shim.hide = this.hideAction;
31914         this.shim.hide();
31915     }else{
31916         this.shim = false;
31917     }
31918     if(this.autoTabs){
31919         this.initTabs();
31920     }
31921     if (this.buttons) { 
31922         var bts= this.buttons;
31923         this.buttons = [];
31924         Roo.each(bts, function(b) {
31925             this.addButton(b);
31926         }, this);
31927     }
31928     
31929     
31930     this.addEvents({
31931         /**
31932          * @event keydown
31933          * Fires when a key is pressed
31934          * @param {Roo.BasicDialog} this
31935          * @param {Roo.EventObject} e
31936          */
31937         "keydown" : true,
31938         /**
31939          * @event move
31940          * Fires when this dialog is moved by the user.
31941          * @param {Roo.BasicDialog} this
31942          * @param {Number} x The new page X
31943          * @param {Number} y The new page Y
31944          */
31945         "move" : true,
31946         /**
31947          * @event resize
31948          * Fires when this dialog is resized by the user.
31949          * @param {Roo.BasicDialog} this
31950          * @param {Number} width The new width
31951          * @param {Number} height The new height
31952          */
31953         "resize" : true,
31954         /**
31955          * @event beforehide
31956          * Fires before this dialog is hidden.
31957          * @param {Roo.BasicDialog} this
31958          */
31959         "beforehide" : true,
31960         /**
31961          * @event hide
31962          * Fires when this dialog is hidden.
31963          * @param {Roo.BasicDialog} this
31964          */
31965         "hide" : true,
31966         /**
31967          * @event beforeshow
31968          * Fires before this dialog is shown.
31969          * @param {Roo.BasicDialog} this
31970          */
31971         "beforeshow" : true,
31972         /**
31973          * @event show
31974          * Fires when this dialog is shown.
31975          * @param {Roo.BasicDialog} this
31976          */
31977         "show" : true
31978     });
31979     el.on("keydown", this.onKeyDown, this);
31980     el.on("mousedown", this.toFront, this);
31981     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
31982     this.el.hide();
31983     Roo.DialogManager.register(this);
31984     Roo.BasicDialog.superclass.constructor.call(this);
31985 };
31986
31987 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
31988     shadowOffset: Roo.isIE ? 6 : 5,
31989     minHeight: 80,
31990     minWidth: 200,
31991     minButtonWidth: 75,
31992     defaultButton: null,
31993     buttonAlign: "right",
31994     tabTag: 'div',
31995     firstShow: true,
31996
31997     /**
31998      * Sets the dialog title text
31999      * @param {String} text The title text to display
32000      * @return {Roo.BasicDialog} this
32001      */
32002     setTitle : function(text){
32003         this.header.update(text);
32004         return this;
32005     },
32006
32007     // private
32008     closeClick : function(){
32009         this.hide();
32010     },
32011
32012     // private
32013     collapseClick : function(){
32014         this[this.collapsed ? "expand" : "collapse"]();
32015     },
32016
32017     /**
32018      * Collapses the dialog to its minimized state (only the title bar is visible).
32019      * Equivalent to the user clicking the collapse dialog button.
32020      */
32021     collapse : function(){
32022         if(!this.collapsed){
32023             this.collapsed = true;
32024             this.el.addClass("x-dlg-collapsed");
32025             this.restoreHeight = this.el.getHeight();
32026             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32027         }
32028     },
32029
32030     /**
32031      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32032      * clicking the expand dialog button.
32033      */
32034     expand : function(){
32035         if(this.collapsed){
32036             this.collapsed = false;
32037             this.el.removeClass("x-dlg-collapsed");
32038             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32039         }
32040     },
32041
32042     /**
32043      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32044      * @return {Roo.TabPanel} The tabs component
32045      */
32046     initTabs : function(){
32047         var tabs = this.getTabs();
32048         while(tabs.getTab(0)){
32049             tabs.removeTab(0);
32050         }
32051         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32052             var dom = el.dom;
32053             tabs.addTab(Roo.id(dom), dom.title);
32054             dom.title = "";
32055         });
32056         tabs.activate(0);
32057         return tabs;
32058     },
32059
32060     // private
32061     beforeResize : function(){
32062         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32063     },
32064
32065     // private
32066     onResize : function(){
32067         this.refreshSize();
32068         this.syncBodyHeight();
32069         this.adjustAssets();
32070         this.focus();
32071         this.fireEvent("resize", this, this.size.width, this.size.height);
32072     },
32073
32074     // private
32075     onKeyDown : function(e){
32076         if(this.isVisible()){
32077             this.fireEvent("keydown", this, e);
32078         }
32079     },
32080
32081     /**
32082      * Resizes the dialog.
32083      * @param {Number} width
32084      * @param {Number} height
32085      * @return {Roo.BasicDialog} this
32086      */
32087     resizeTo : function(width, height){
32088         this.el.setSize(width, height);
32089         this.size = {width: width, height: height};
32090         this.syncBodyHeight();
32091         if(this.fixedcenter){
32092             this.center();
32093         }
32094         if(this.isVisible()){
32095             this.constrainXY();
32096             this.adjustAssets();
32097         }
32098         this.fireEvent("resize", this, width, height);
32099         return this;
32100     },
32101
32102
32103     /**
32104      * Resizes the dialog to fit the specified content size.
32105      * @param {Number} width
32106      * @param {Number} height
32107      * @return {Roo.BasicDialog} this
32108      */
32109     setContentSize : function(w, h){
32110         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32111         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32112         //if(!this.el.isBorderBox()){
32113             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32114             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32115         //}
32116         if(this.tabs){
32117             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32118             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32119         }
32120         this.resizeTo(w, h);
32121         return this;
32122     },
32123
32124     /**
32125      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32126      * executed in response to a particular key being pressed while the dialog is active.
32127      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32128      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32129      * @param {Function} fn The function to call
32130      * @param {Object} scope (optional) The scope of the function
32131      * @return {Roo.BasicDialog} this
32132      */
32133     addKeyListener : function(key, fn, scope){
32134         var keyCode, shift, ctrl, alt;
32135         if(typeof key == "object" && !(key instanceof Array)){
32136             keyCode = key["key"];
32137             shift = key["shift"];
32138             ctrl = key["ctrl"];
32139             alt = key["alt"];
32140         }else{
32141             keyCode = key;
32142         }
32143         var handler = function(dlg, e){
32144             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32145                 var k = e.getKey();
32146                 if(keyCode instanceof Array){
32147                     for(var i = 0, len = keyCode.length; i < len; i++){
32148                         if(keyCode[i] == k){
32149                           fn.call(scope || window, dlg, k, e);
32150                           return;
32151                         }
32152                     }
32153                 }else{
32154                     if(k == keyCode){
32155                         fn.call(scope || window, dlg, k, e);
32156                     }
32157                 }
32158             }
32159         };
32160         this.on("keydown", handler);
32161         return this;
32162     },
32163
32164     /**
32165      * Returns the TabPanel component (creates it if it doesn't exist).
32166      * Note: If you wish to simply check for the existence of tabs without creating them,
32167      * check for a null 'tabs' property.
32168      * @return {Roo.TabPanel} The tabs component
32169      */
32170     getTabs : function(){
32171         if(!this.tabs){
32172             this.el.addClass("x-dlg-auto-tabs");
32173             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32174             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32175         }
32176         return this.tabs;
32177     },
32178
32179     /**
32180      * Adds a button to the footer section of the dialog.
32181      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32182      * object or a valid Roo.DomHelper element config
32183      * @param {Function} handler The function called when the button is clicked
32184      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32185      * @return {Roo.Button} The new button
32186      */
32187     addButton : function(config, handler, scope){
32188         var dh = Roo.DomHelper;
32189         if(!this.footer){
32190             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32191         }
32192         if(!this.btnContainer){
32193             var tb = this.footer.createChild({
32194
32195                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32196                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32197             }, null, true);
32198             this.btnContainer = tb.firstChild.firstChild.firstChild;
32199         }
32200         var bconfig = {
32201             handler: handler,
32202             scope: scope,
32203             minWidth: this.minButtonWidth,
32204             hideParent:true
32205         };
32206         if(typeof config == "string"){
32207             bconfig.text = config;
32208         }else{
32209             if(config.tag){
32210                 bconfig.dhconfig = config;
32211             }else{
32212                 Roo.apply(bconfig, config);
32213             }
32214         }
32215         var fc = false;
32216         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32217             bconfig.position = Math.max(0, bconfig.position);
32218             fc = this.btnContainer.childNodes[bconfig.position];
32219         }
32220          
32221         var btn = new Roo.Button(
32222             fc ? 
32223                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32224                 : this.btnContainer.appendChild(document.createElement("td")),
32225             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32226             bconfig
32227         );
32228         this.syncBodyHeight();
32229         if(!this.buttons){
32230             /**
32231              * Array of all the buttons that have been added to this dialog via addButton
32232              * @type Array
32233              */
32234             this.buttons = [];
32235         }
32236         this.buttons.push(btn);
32237         return btn;
32238     },
32239
32240     /**
32241      * Sets the default button to be focused when the dialog is displayed.
32242      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32243      * @return {Roo.BasicDialog} this
32244      */
32245     setDefaultButton : function(btn){
32246         this.defaultButton = btn;
32247         return this;
32248     },
32249
32250     // private
32251     getHeaderFooterHeight : function(safe){
32252         var height = 0;
32253         if(this.header){
32254            height += this.header.getHeight();
32255         }
32256         if(this.footer){
32257            var fm = this.footer.getMargins();
32258             height += (this.footer.getHeight()+fm.top+fm.bottom);
32259         }
32260         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32261         height += this.centerBg.getPadding("tb");
32262         return height;
32263     },
32264
32265     // private
32266     syncBodyHeight : function()
32267     {
32268         var bd = this.body, // the text
32269             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32270             bw = this.bwrap;
32271         var height = this.size.height - this.getHeaderFooterHeight(false);
32272         bd.setHeight(height-bd.getMargins("tb"));
32273         var hh = this.header.getHeight();
32274         var h = this.size.height-hh;
32275         cb.setHeight(h);
32276         
32277         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32278         bw.setHeight(h-cb.getPadding("tb"));
32279         
32280         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32281         bd.setWidth(bw.getWidth(true));
32282         if(this.tabs){
32283             this.tabs.syncHeight();
32284             if(Roo.isIE){
32285                 this.tabs.el.repaint();
32286             }
32287         }
32288     },
32289
32290     /**
32291      * Restores the previous state of the dialog if Roo.state is configured.
32292      * @return {Roo.BasicDialog} this
32293      */
32294     restoreState : function(){
32295         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32296         if(box && box.width){
32297             this.xy = [box.x, box.y];
32298             this.resizeTo(box.width, box.height);
32299         }
32300         return this;
32301     },
32302
32303     // private
32304     beforeShow : function(){
32305         this.expand();
32306         if(this.fixedcenter){
32307             this.xy = this.el.getCenterXY(true);
32308         }
32309         if(this.modal){
32310             Roo.get(document.body).addClass("x-body-masked");
32311             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32312             this.mask.show();
32313         }
32314         this.constrainXY();
32315     },
32316
32317     // private
32318     animShow : function(){
32319         var b = Roo.get(this.animateTarget).getBox();
32320         this.proxy.setSize(b.width, b.height);
32321         this.proxy.setLocation(b.x, b.y);
32322         this.proxy.show();
32323         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32324                     true, .35, this.showEl.createDelegate(this));
32325     },
32326
32327     /**
32328      * Shows the dialog.
32329      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32330      * @return {Roo.BasicDialog} this
32331      */
32332     show : function(animateTarget){
32333         if (this.fireEvent("beforeshow", this) === false){
32334             return;
32335         }
32336         if(this.syncHeightBeforeShow){
32337             this.syncBodyHeight();
32338         }else if(this.firstShow){
32339             this.firstShow = false;
32340             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32341         }
32342         this.animateTarget = animateTarget || this.animateTarget;
32343         if(!this.el.isVisible()){
32344             this.beforeShow();
32345             if(this.animateTarget && Roo.get(this.animateTarget)){
32346                 this.animShow();
32347             }else{
32348                 this.showEl();
32349             }
32350         }
32351         return this;
32352     },
32353
32354     // private
32355     showEl : function(){
32356         this.proxy.hide();
32357         this.el.setXY(this.xy);
32358         this.el.show();
32359         this.adjustAssets(true);
32360         this.toFront();
32361         this.focus();
32362         // IE peekaboo bug - fix found by Dave Fenwick
32363         if(Roo.isIE){
32364             this.el.repaint();
32365         }
32366         this.fireEvent("show", this);
32367     },
32368
32369     /**
32370      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32371      * dialog itself will receive focus.
32372      */
32373     focus : function(){
32374         if(this.defaultButton){
32375             this.defaultButton.focus();
32376         }else{
32377             this.focusEl.focus();
32378         }
32379     },
32380
32381     // private
32382     constrainXY : function(){
32383         if(this.constraintoviewport !== false){
32384             if(!this.viewSize){
32385                 if(this.container){
32386                     var s = this.container.getSize();
32387                     this.viewSize = [s.width, s.height];
32388                 }else{
32389                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32390                 }
32391             }
32392             var s = Roo.get(this.container||document).getScroll();
32393
32394             var x = this.xy[0], y = this.xy[1];
32395             var w = this.size.width, h = this.size.height;
32396             var vw = this.viewSize[0], vh = this.viewSize[1];
32397             // only move it if it needs it
32398             var moved = false;
32399             // first validate right/bottom
32400             if(x + w > vw+s.left){
32401                 x = vw - w;
32402                 moved = true;
32403             }
32404             if(y + h > vh+s.top){
32405                 y = vh - h;
32406                 moved = true;
32407             }
32408             // then make sure top/left isn't negative
32409             if(x < s.left){
32410                 x = s.left;
32411                 moved = true;
32412             }
32413             if(y < s.top){
32414                 y = s.top;
32415                 moved = true;
32416             }
32417             if(moved){
32418                 // cache xy
32419                 this.xy = [x, y];
32420                 if(this.isVisible()){
32421                     this.el.setLocation(x, y);
32422                     this.adjustAssets();
32423                 }
32424             }
32425         }
32426     },
32427
32428     // private
32429     onDrag : function(){
32430         if(!this.proxyDrag){
32431             this.xy = this.el.getXY();
32432             this.adjustAssets();
32433         }
32434     },
32435
32436     // private
32437     adjustAssets : function(doShow){
32438         var x = this.xy[0], y = this.xy[1];
32439         var w = this.size.width, h = this.size.height;
32440         if(doShow === true){
32441             if(this.shadow){
32442                 this.shadow.show(this.el);
32443             }
32444             if(this.shim){
32445                 this.shim.show();
32446             }
32447         }
32448         if(this.shadow && this.shadow.isVisible()){
32449             this.shadow.show(this.el);
32450         }
32451         if(this.shim && this.shim.isVisible()){
32452             this.shim.setBounds(x, y, w, h);
32453         }
32454     },
32455
32456     // private
32457     adjustViewport : function(w, h){
32458         if(!w || !h){
32459             w = Roo.lib.Dom.getViewWidth();
32460             h = Roo.lib.Dom.getViewHeight();
32461         }
32462         // cache the size
32463         this.viewSize = [w, h];
32464         if(this.modal && this.mask.isVisible()){
32465             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32466             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32467         }
32468         if(this.isVisible()){
32469             this.constrainXY();
32470         }
32471     },
32472
32473     /**
32474      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32475      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32476      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32477      */
32478     destroy : function(removeEl){
32479         if(this.isVisible()){
32480             this.animateTarget = null;
32481             this.hide();
32482         }
32483         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32484         if(this.tabs){
32485             this.tabs.destroy(removeEl);
32486         }
32487         Roo.destroy(
32488              this.shim,
32489              this.proxy,
32490              this.resizer,
32491              this.close,
32492              this.mask
32493         );
32494         if(this.dd){
32495             this.dd.unreg();
32496         }
32497         if(this.buttons){
32498            for(var i = 0, len = this.buttons.length; i < len; i++){
32499                this.buttons[i].destroy();
32500            }
32501         }
32502         this.el.removeAllListeners();
32503         if(removeEl === true){
32504             this.el.update("");
32505             this.el.remove();
32506         }
32507         Roo.DialogManager.unregister(this);
32508     },
32509
32510     // private
32511     startMove : function(){
32512         if(this.proxyDrag){
32513             this.proxy.show();
32514         }
32515         if(this.constraintoviewport !== false){
32516             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32517         }
32518     },
32519
32520     // private
32521     endMove : function(){
32522         if(!this.proxyDrag){
32523             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32524         }else{
32525             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32526             this.proxy.hide();
32527         }
32528         this.refreshSize();
32529         this.adjustAssets();
32530         this.focus();
32531         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32532     },
32533
32534     /**
32535      * Brings this dialog to the front of any other visible dialogs
32536      * @return {Roo.BasicDialog} this
32537      */
32538     toFront : function(){
32539         Roo.DialogManager.bringToFront(this);
32540         return this;
32541     },
32542
32543     /**
32544      * Sends this dialog to the back (under) of any other visible dialogs
32545      * @return {Roo.BasicDialog} this
32546      */
32547     toBack : function(){
32548         Roo.DialogManager.sendToBack(this);
32549         return this;
32550     },
32551
32552     /**
32553      * Centers this dialog in the viewport
32554      * @return {Roo.BasicDialog} this
32555      */
32556     center : function(){
32557         var xy = this.el.getCenterXY(true);
32558         this.moveTo(xy[0], xy[1]);
32559         return this;
32560     },
32561
32562     /**
32563      * Moves the dialog's top-left corner to the specified point
32564      * @param {Number} x
32565      * @param {Number} y
32566      * @return {Roo.BasicDialog} this
32567      */
32568     moveTo : function(x, y){
32569         this.xy = [x,y];
32570         if(this.isVisible()){
32571             this.el.setXY(this.xy);
32572             this.adjustAssets();
32573         }
32574         return this;
32575     },
32576
32577     /**
32578      * Aligns the dialog to the specified element
32579      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32580      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32581      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32582      * @return {Roo.BasicDialog} this
32583      */
32584     alignTo : function(element, position, offsets){
32585         this.xy = this.el.getAlignToXY(element, position, offsets);
32586         if(this.isVisible()){
32587             this.el.setXY(this.xy);
32588             this.adjustAssets();
32589         }
32590         return this;
32591     },
32592
32593     /**
32594      * Anchors an element to another element and realigns it when the window is resized.
32595      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32596      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32597      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32598      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32599      * is a number, it is used as the buffer delay (defaults to 50ms).
32600      * @return {Roo.BasicDialog} this
32601      */
32602     anchorTo : function(el, alignment, offsets, monitorScroll){
32603         var action = function(){
32604             this.alignTo(el, alignment, offsets);
32605         };
32606         Roo.EventManager.onWindowResize(action, this);
32607         var tm = typeof monitorScroll;
32608         if(tm != 'undefined'){
32609             Roo.EventManager.on(window, 'scroll', action, this,
32610                 {buffer: tm == 'number' ? monitorScroll : 50});
32611         }
32612         action.call(this);
32613         return this;
32614     },
32615
32616     /**
32617      * Returns true if the dialog is visible
32618      * @return {Boolean}
32619      */
32620     isVisible : function(){
32621         return this.el.isVisible();
32622     },
32623
32624     // private
32625     animHide : function(callback){
32626         var b = Roo.get(this.animateTarget).getBox();
32627         this.proxy.show();
32628         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32629         this.el.hide();
32630         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32631                     this.hideEl.createDelegate(this, [callback]));
32632     },
32633
32634     /**
32635      * Hides the dialog.
32636      * @param {Function} callback (optional) Function to call when the dialog is hidden
32637      * @return {Roo.BasicDialog} this
32638      */
32639     hide : function(callback){
32640         if (this.fireEvent("beforehide", this) === false){
32641             return;
32642         }
32643         if(this.shadow){
32644             this.shadow.hide();
32645         }
32646         if(this.shim) {
32647           this.shim.hide();
32648         }
32649         // sometimes animateTarget seems to get set.. causing problems...
32650         // this just double checks..
32651         if(this.animateTarget && Roo.get(this.animateTarget)) {
32652            this.animHide(callback);
32653         }else{
32654             this.el.hide();
32655             this.hideEl(callback);
32656         }
32657         return this;
32658     },
32659
32660     // private
32661     hideEl : function(callback){
32662         this.proxy.hide();
32663         if(this.modal){
32664             this.mask.hide();
32665             Roo.get(document.body).removeClass("x-body-masked");
32666         }
32667         this.fireEvent("hide", this);
32668         if(typeof callback == "function"){
32669             callback();
32670         }
32671     },
32672
32673     // private
32674     hideAction : function(){
32675         this.setLeft("-10000px");
32676         this.setTop("-10000px");
32677         this.setStyle("visibility", "hidden");
32678     },
32679
32680     // private
32681     refreshSize : function(){
32682         this.size = this.el.getSize();
32683         this.xy = this.el.getXY();
32684         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32685     },
32686
32687     // private
32688     // z-index is managed by the DialogManager and may be overwritten at any time
32689     setZIndex : function(index){
32690         if(this.modal){
32691             this.mask.setStyle("z-index", index);
32692         }
32693         if(this.shim){
32694             this.shim.setStyle("z-index", ++index);
32695         }
32696         if(this.shadow){
32697             this.shadow.setZIndex(++index);
32698         }
32699         this.el.setStyle("z-index", ++index);
32700         if(this.proxy){
32701             this.proxy.setStyle("z-index", ++index);
32702         }
32703         if(this.resizer){
32704             this.resizer.proxy.setStyle("z-index", ++index);
32705         }
32706
32707         this.lastZIndex = index;
32708     },
32709
32710     /**
32711      * Returns the element for this dialog
32712      * @return {Roo.Element} The underlying dialog Element
32713      */
32714     getEl : function(){
32715         return this.el;
32716     }
32717 });
32718
32719 /**
32720  * @class Roo.DialogManager
32721  * Provides global access to BasicDialogs that have been created and
32722  * support for z-indexing (layering) multiple open dialogs.
32723  */
32724 Roo.DialogManager = function(){
32725     var list = {};
32726     var accessList = [];
32727     var front = null;
32728
32729     // private
32730     var sortDialogs = function(d1, d2){
32731         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32732     };
32733
32734     // private
32735     var orderDialogs = function(){
32736         accessList.sort(sortDialogs);
32737         var seed = Roo.DialogManager.zseed;
32738         for(var i = 0, len = accessList.length; i < len; i++){
32739             var dlg = accessList[i];
32740             if(dlg){
32741                 dlg.setZIndex(seed + (i*10));
32742             }
32743         }
32744     };
32745
32746     return {
32747         /**
32748          * The starting z-index for BasicDialogs (defaults to 9000)
32749          * @type Number The z-index value
32750          */
32751         zseed : 9000,
32752
32753         // private
32754         register : function(dlg){
32755             list[dlg.id] = dlg;
32756             accessList.push(dlg);
32757         },
32758
32759         // private
32760         unregister : function(dlg){
32761             delete list[dlg.id];
32762             var i=0;
32763             var len=0;
32764             if(!accessList.indexOf){
32765                 for(  i = 0, len = accessList.length; i < len; i++){
32766                     if(accessList[i] == dlg){
32767                         accessList.splice(i, 1);
32768                         return;
32769                     }
32770                 }
32771             }else{
32772                  i = accessList.indexOf(dlg);
32773                 if(i != -1){
32774                     accessList.splice(i, 1);
32775                 }
32776             }
32777         },
32778
32779         /**
32780          * Gets a registered dialog by id
32781          * @param {String/Object} id The id of the dialog or a dialog
32782          * @return {Roo.BasicDialog} this
32783          */
32784         get : function(id){
32785             return typeof id == "object" ? id : list[id];
32786         },
32787
32788         /**
32789          * Brings the specified dialog to the front
32790          * @param {String/Object} dlg The id of the dialog or a dialog
32791          * @return {Roo.BasicDialog} this
32792          */
32793         bringToFront : function(dlg){
32794             dlg = this.get(dlg);
32795             if(dlg != front){
32796                 front = dlg;
32797                 dlg._lastAccess = new Date().getTime();
32798                 orderDialogs();
32799             }
32800             return dlg;
32801         },
32802
32803         /**
32804          * Sends the specified dialog to the back
32805          * @param {String/Object} dlg The id of the dialog or a dialog
32806          * @return {Roo.BasicDialog} this
32807          */
32808         sendToBack : function(dlg){
32809             dlg = this.get(dlg);
32810             dlg._lastAccess = -(new Date().getTime());
32811             orderDialogs();
32812             return dlg;
32813         },
32814
32815         /**
32816          * Hides all dialogs
32817          */
32818         hideAll : function(){
32819             for(var id in list){
32820                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32821                     list[id].hide();
32822                 }
32823             }
32824         }
32825     };
32826 }();
32827
32828 /**
32829  * @class Roo.LayoutDialog
32830  * @extends Roo.BasicDialog
32831  * Dialog which provides adjustments for working with a layout in a Dialog.
32832  * Add your necessary layout config options to the dialog's config.<br>
32833  * Example usage (including a nested layout):
32834  * <pre><code>
32835 if(!dialog){
32836     dialog = new Roo.LayoutDialog("download-dlg", {
32837         modal: true,
32838         width:600,
32839         height:450,
32840         shadow:true,
32841         minWidth:500,
32842         minHeight:350,
32843         autoTabs:true,
32844         proxyDrag:true,
32845         // layout config merges with the dialog config
32846         center:{
32847             tabPosition: "top",
32848             alwaysShowTabs: true
32849         }
32850     });
32851     dialog.addKeyListener(27, dialog.hide, dialog);
32852     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32853     dialog.addButton("Build It!", this.getDownload, this);
32854
32855     // we can even add nested layouts
32856     var innerLayout = new Roo.BorderLayout("dl-inner", {
32857         east: {
32858             initialSize: 200,
32859             autoScroll:true,
32860             split:true
32861         },
32862         center: {
32863             autoScroll:true
32864         }
32865     });
32866     innerLayout.beginUpdate();
32867     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32868     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32869     innerLayout.endUpdate(true);
32870
32871     var layout = dialog.getLayout();
32872     layout.beginUpdate();
32873     layout.add("center", new Roo.ContentPanel("standard-panel",
32874                         {title: "Download the Source", fitToFrame:true}));
32875     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32876                {title: "Build your own roo.js"}));
32877     layout.getRegion("center").showPanel(sp);
32878     layout.endUpdate();
32879 }
32880 </code></pre>
32881     * @constructor
32882     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32883     * @param {Object} config configuration options
32884   */
32885 Roo.LayoutDialog = function(el, cfg){
32886     
32887     var config=  cfg;
32888     if (typeof(cfg) == 'undefined') {
32889         config = Roo.apply({}, el);
32890         // not sure why we use documentElement here.. - it should always be body.
32891         // IE7 borks horribly if we use documentElement.
32892         // webkit also does not like documentElement - it creates a body element...
32893         el = Roo.get( document.body || document.documentElement ).createChild();
32894         //config.autoCreate = true;
32895     }
32896     
32897     
32898     config.autoTabs = false;
32899     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
32900     this.body.setStyle({overflow:"hidden", position:"relative"});
32901     this.layout = new Roo.BorderLayout(this.body.dom, config);
32902     this.layout.monitorWindowResize = false;
32903     this.el.addClass("x-dlg-auto-layout");
32904     // fix case when center region overwrites center function
32905     this.center = Roo.BasicDialog.prototype.center;
32906     this.on("show", this.layout.layout, this.layout, true);
32907     if (config.items) {
32908         var xitems = config.items;
32909         delete config.items;
32910         Roo.each(xitems, this.addxtype, this);
32911     }
32912     
32913     
32914 };
32915 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
32916     /**
32917      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
32918      * @deprecated
32919      */
32920     endUpdate : function(){
32921         this.layout.endUpdate();
32922     },
32923
32924     /**
32925      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
32926      *  @deprecated
32927      */
32928     beginUpdate : function(){
32929         this.layout.beginUpdate();
32930     },
32931
32932     /**
32933      * Get the BorderLayout for this dialog
32934      * @return {Roo.BorderLayout}
32935      */
32936     getLayout : function(){
32937         return this.layout;
32938     },
32939
32940     showEl : function(){
32941         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
32942         if(Roo.isIE7){
32943             this.layout.layout();
32944         }
32945     },
32946
32947     // private
32948     // Use the syncHeightBeforeShow config option to control this automatically
32949     syncBodyHeight : function(){
32950         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
32951         if(this.layout){this.layout.layout();}
32952     },
32953     
32954       /**
32955      * Add an xtype element (actually adds to the layout.)
32956      * @return {Object} xdata xtype object data.
32957      */
32958     
32959     addxtype : function(c) {
32960         return this.layout.addxtype(c);
32961     }
32962 });/*
32963  * Based on:
32964  * Ext JS Library 1.1.1
32965  * Copyright(c) 2006-2007, Ext JS, LLC.
32966  *
32967  * Originally Released Under LGPL - original licence link has changed is not relivant.
32968  *
32969  * Fork - LGPL
32970  * <script type="text/javascript">
32971  */
32972  
32973 /**
32974  * @class Roo.MessageBox
32975  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
32976  * Example usage:
32977  *<pre><code>
32978 // Basic alert:
32979 Roo.Msg.alert('Status', 'Changes saved successfully.');
32980
32981 // Prompt for user data:
32982 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
32983     if (btn == 'ok'){
32984         // process text value...
32985     }
32986 });
32987
32988 // Show a dialog using config options:
32989 Roo.Msg.show({
32990    title:'Save Changes?',
32991    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
32992    buttons: Roo.Msg.YESNOCANCEL,
32993    fn: processResult,
32994    animEl: 'elId'
32995 });
32996 </code></pre>
32997  * @singleton
32998  */
32999 Roo.MessageBox = function(){
33000     var dlg, opt, mask, waitTimer;
33001     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33002     var buttons, activeTextEl, bwidth;
33003
33004     // private
33005     var handleButton = function(button){
33006         dlg.hide();
33007         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33008     };
33009
33010     // private
33011     var handleHide = function(){
33012         if(opt && opt.cls){
33013             dlg.el.removeClass(opt.cls);
33014         }
33015         if(waitTimer){
33016             Roo.TaskMgr.stop(waitTimer);
33017             waitTimer = null;
33018         }
33019     };
33020
33021     // private
33022     var updateButtons = function(b){
33023         var width = 0;
33024         if(!b){
33025             buttons["ok"].hide();
33026             buttons["cancel"].hide();
33027             buttons["yes"].hide();
33028             buttons["no"].hide();
33029             dlg.footer.dom.style.display = 'none';
33030             return width;
33031         }
33032         dlg.footer.dom.style.display = '';
33033         for(var k in buttons){
33034             if(typeof buttons[k] != "function"){
33035                 if(b[k]){
33036                     buttons[k].show();
33037                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33038                     width += buttons[k].el.getWidth()+15;
33039                 }else{
33040                     buttons[k].hide();
33041                 }
33042             }
33043         }
33044         return width;
33045     };
33046
33047     // private
33048     var handleEsc = function(d, k, e){
33049         if(opt && opt.closable !== false){
33050             dlg.hide();
33051         }
33052         if(e){
33053             e.stopEvent();
33054         }
33055     };
33056
33057     return {
33058         /**
33059          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33060          * @return {Roo.BasicDialog} The BasicDialog element
33061          */
33062         getDialog : function(){
33063            if(!dlg){
33064                 dlg = new Roo.BasicDialog("x-msg-box", {
33065                     autoCreate : true,
33066                     shadow: true,
33067                     draggable: true,
33068                     resizable:false,
33069                     constraintoviewport:false,
33070                     fixedcenter:true,
33071                     collapsible : false,
33072                     shim:true,
33073                     modal: true,
33074                     width:400, height:100,
33075                     buttonAlign:"center",
33076                     closeClick : function(){
33077                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33078                             handleButton("no");
33079                         }else{
33080                             handleButton("cancel");
33081                         }
33082                     }
33083                 });
33084                 dlg.on("hide", handleHide);
33085                 mask = dlg.mask;
33086                 dlg.addKeyListener(27, handleEsc);
33087                 buttons = {};
33088                 var bt = this.buttonText;
33089                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33090                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33091                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33092                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33093                 bodyEl = dlg.body.createChild({
33094
33095                     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>'
33096                 });
33097                 msgEl = bodyEl.dom.firstChild;
33098                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33099                 textboxEl.enableDisplayMode();
33100                 textboxEl.addKeyListener([10,13], function(){
33101                     if(dlg.isVisible() && opt && opt.buttons){
33102                         if(opt.buttons.ok){
33103                             handleButton("ok");
33104                         }else if(opt.buttons.yes){
33105                             handleButton("yes");
33106                         }
33107                     }
33108                 });
33109                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33110                 textareaEl.enableDisplayMode();
33111                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33112                 progressEl.enableDisplayMode();
33113                 var pf = progressEl.dom.firstChild;
33114                 if (pf) {
33115                     pp = Roo.get(pf.firstChild);
33116                     pp.setHeight(pf.offsetHeight);
33117                 }
33118                 
33119             }
33120             return dlg;
33121         },
33122
33123         /**
33124          * Updates the message box body text
33125          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33126          * the XHTML-compliant non-breaking space character '&amp;#160;')
33127          * @return {Roo.MessageBox} This message box
33128          */
33129         updateText : function(text){
33130             if(!dlg.isVisible() && !opt.width){
33131                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33132             }
33133             msgEl.innerHTML = text || '&#160;';
33134       
33135             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33136             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33137             var w = Math.max(
33138                     Math.min(opt.width || cw , this.maxWidth), 
33139                     Math.max(opt.minWidth || this.minWidth, bwidth)
33140             );
33141             if(opt.prompt){
33142                 activeTextEl.setWidth(w);
33143             }
33144             if(dlg.isVisible()){
33145                 dlg.fixedcenter = false;
33146             }
33147             // to big, make it scroll. = But as usual stupid IE does not support
33148             // !important..
33149             
33150             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33151                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33152                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33153             } else {
33154                 bodyEl.dom.style.height = '';
33155                 bodyEl.dom.style.overflowY = '';
33156             }
33157             if (cw > w) {
33158                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33159             } else {
33160                 bodyEl.dom.style.overflowX = '';
33161             }
33162             
33163             dlg.setContentSize(w, bodyEl.getHeight());
33164             if(dlg.isVisible()){
33165                 dlg.fixedcenter = true;
33166             }
33167             return this;
33168         },
33169
33170         /**
33171          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33172          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33173          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33174          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33175          * @return {Roo.MessageBox} This message box
33176          */
33177         updateProgress : function(value, text){
33178             if(text){
33179                 this.updateText(text);
33180             }
33181             if (pp) { // weird bug on my firefox - for some reason this is not defined
33182                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33183             }
33184             return this;
33185         },        
33186
33187         /**
33188          * Returns true if the message box is currently displayed
33189          * @return {Boolean} True if the message box is visible, else false
33190          */
33191         isVisible : function(){
33192             return dlg && dlg.isVisible();  
33193         },
33194
33195         /**
33196          * Hides the message box if it is displayed
33197          */
33198         hide : function(){
33199             if(this.isVisible()){
33200                 dlg.hide();
33201             }  
33202         },
33203
33204         /**
33205          * Displays a new message box, or reinitializes an existing message box, based on the config options
33206          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33207          * The following config object properties are supported:
33208          * <pre>
33209 Property    Type             Description
33210 ----------  ---------------  ------------------------------------------------------------------------------------
33211 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33212                                    closes (defaults to undefined)
33213 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33214                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33215 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33216                                    progress and wait dialogs will ignore this property and always hide the
33217                                    close button as they can only be closed programmatically.
33218 cls               String           A custom CSS class to apply to the message box element
33219 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33220                                    displayed (defaults to 75)
33221 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33222                                    function will be btn (the name of the button that was clicked, if applicable,
33223                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33224                                    Progress and wait dialogs will ignore this option since they do not respond to
33225                                    user actions and can only be closed programmatically, so any required function
33226                                    should be called by the same code after it closes the dialog.
33227 icon              String           A CSS class that provides a background image to be used as an icon for
33228                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33229 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33230 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33231 modal             Boolean          False to allow user interaction with the page while the message box is
33232                                    displayed (defaults to true)
33233 msg               String           A string that will replace the existing message box body text (defaults
33234                                    to the XHTML-compliant non-breaking space character '&#160;')
33235 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33236 progress          Boolean          True to display a progress bar (defaults to false)
33237 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33238 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33239 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33240 title             String           The title text
33241 value             String           The string value to set into the active textbox element if displayed
33242 wait              Boolean          True to display a progress bar (defaults to false)
33243 width             Number           The width of the dialog in pixels
33244 </pre>
33245          *
33246          * Example usage:
33247          * <pre><code>
33248 Roo.Msg.show({
33249    title: 'Address',
33250    msg: 'Please enter your address:',
33251    width: 300,
33252    buttons: Roo.MessageBox.OKCANCEL,
33253    multiline: true,
33254    fn: saveAddress,
33255    animEl: 'addAddressBtn'
33256 });
33257 </code></pre>
33258          * @param {Object} config Configuration options
33259          * @return {Roo.MessageBox} This message box
33260          */
33261         show : function(options)
33262         {
33263             
33264             // this causes nightmares if you show one dialog after another
33265             // especially on callbacks..
33266              
33267             if(this.isVisible()){
33268                 
33269                 this.hide();
33270                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33271                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33272                 Roo.log("New Dialog Message:" +  options.msg )
33273                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33274                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33275                 
33276             }
33277             var d = this.getDialog();
33278             opt = options;
33279             d.setTitle(opt.title || "&#160;");
33280             d.close.setDisplayed(opt.closable !== false);
33281             activeTextEl = textboxEl;
33282             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33283             if(opt.prompt){
33284                 if(opt.multiline){
33285                     textboxEl.hide();
33286                     textareaEl.show();
33287                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33288                         opt.multiline : this.defaultTextHeight);
33289                     activeTextEl = textareaEl;
33290                 }else{
33291                     textboxEl.show();
33292                     textareaEl.hide();
33293                 }
33294             }else{
33295                 textboxEl.hide();
33296                 textareaEl.hide();
33297             }
33298             progressEl.setDisplayed(opt.progress === true);
33299             this.updateProgress(0);
33300             activeTextEl.dom.value = opt.value || "";
33301             if(opt.prompt){
33302                 dlg.setDefaultButton(activeTextEl);
33303             }else{
33304                 var bs = opt.buttons;
33305                 var db = null;
33306                 if(bs && bs.ok){
33307                     db = buttons["ok"];
33308                 }else if(bs && bs.yes){
33309                     db = buttons["yes"];
33310                 }
33311                 dlg.setDefaultButton(db);
33312             }
33313             bwidth = updateButtons(opt.buttons);
33314             this.updateText(opt.msg);
33315             if(opt.cls){
33316                 d.el.addClass(opt.cls);
33317             }
33318             d.proxyDrag = opt.proxyDrag === true;
33319             d.modal = opt.modal !== false;
33320             d.mask = opt.modal !== false ? mask : false;
33321             if(!d.isVisible()){
33322                 // force it to the end of the z-index stack so it gets a cursor in FF
33323                 document.body.appendChild(dlg.el.dom);
33324                 d.animateTarget = null;
33325                 d.show(options.animEl);
33326             }
33327             return this;
33328         },
33329
33330         /**
33331          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33332          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33333          * and closing the message box when the process is complete.
33334          * @param {String} title The title bar text
33335          * @param {String} msg The message box body text
33336          * @return {Roo.MessageBox} This message box
33337          */
33338         progress : function(title, msg){
33339             this.show({
33340                 title : title,
33341                 msg : msg,
33342                 buttons: false,
33343                 progress:true,
33344                 closable:false,
33345                 minWidth: this.minProgressWidth,
33346                 modal : true
33347             });
33348             return this;
33349         },
33350
33351         /**
33352          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33353          * If a callback function is passed it will be called after the user clicks the button, and the
33354          * id of the button that was clicked will be passed as the only parameter to the callback
33355          * (could also be the top-right close button).
33356          * @param {String} title The title bar text
33357          * @param {String} msg The message box body text
33358          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33359          * @param {Object} scope (optional) The scope of the callback function
33360          * @return {Roo.MessageBox} This message box
33361          */
33362         alert : function(title, msg, fn, scope){
33363             this.show({
33364                 title : title,
33365                 msg : msg,
33366                 buttons: this.OK,
33367                 fn: fn,
33368                 scope : scope,
33369                 modal : true
33370             });
33371             return this;
33372         },
33373
33374         /**
33375          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33376          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33377          * You are responsible for closing the message box when the process is complete.
33378          * @param {String} msg The message box body text
33379          * @param {String} title (optional) The title bar text
33380          * @return {Roo.MessageBox} This message box
33381          */
33382         wait : function(msg, title){
33383             this.show({
33384                 title : title,
33385                 msg : msg,
33386                 buttons: false,
33387                 closable:false,
33388                 progress:true,
33389                 modal:true,
33390                 width:300,
33391                 wait:true
33392             });
33393             waitTimer = Roo.TaskMgr.start({
33394                 run: function(i){
33395                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33396                 },
33397                 interval: 1000
33398             });
33399             return this;
33400         },
33401
33402         /**
33403          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33404          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33405          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33406          * @param {String} title The title bar text
33407          * @param {String} msg The message box body text
33408          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33409          * @param {Object} scope (optional) The scope of the callback function
33410          * @return {Roo.MessageBox} This message box
33411          */
33412         confirm : function(title, msg, fn, scope){
33413             this.show({
33414                 title : title,
33415                 msg : msg,
33416                 buttons: this.YESNO,
33417                 fn: fn,
33418                 scope : scope,
33419                 modal : true
33420             });
33421             return this;
33422         },
33423
33424         /**
33425          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33426          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33427          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33428          * (could also be the top-right close button) and the text that was entered will be passed as the two
33429          * parameters to the callback.
33430          * @param {String} title The title bar text
33431          * @param {String} msg The message box body text
33432          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33433          * @param {Object} scope (optional) The scope of the callback function
33434          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33435          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33436          * @return {Roo.MessageBox} This message box
33437          */
33438         prompt : function(title, msg, fn, scope, multiline){
33439             this.show({
33440                 title : title,
33441                 msg : msg,
33442                 buttons: this.OKCANCEL,
33443                 fn: fn,
33444                 minWidth:250,
33445                 scope : scope,
33446                 prompt:true,
33447                 multiline: multiline,
33448                 modal : true
33449             });
33450             return this;
33451         },
33452
33453         /**
33454          * Button config that displays a single OK button
33455          * @type Object
33456          */
33457         OK : {ok:true},
33458         /**
33459          * Button config that displays Yes and No buttons
33460          * @type Object
33461          */
33462         YESNO : {yes:true, no:true},
33463         /**
33464          * Button config that displays OK and Cancel buttons
33465          * @type Object
33466          */
33467         OKCANCEL : {ok:true, cancel:true},
33468         /**
33469          * Button config that displays Yes, No and Cancel buttons
33470          * @type Object
33471          */
33472         YESNOCANCEL : {yes:true, no:true, cancel:true},
33473
33474         /**
33475          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33476          * @type Number
33477          */
33478         defaultTextHeight : 75,
33479         /**
33480          * The maximum width in pixels of the message box (defaults to 600)
33481          * @type Number
33482          */
33483         maxWidth : 600,
33484         /**
33485          * The minimum width in pixels of the message box (defaults to 100)
33486          * @type Number
33487          */
33488         minWidth : 100,
33489         /**
33490          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33491          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33492          * @type Number
33493          */
33494         minProgressWidth : 250,
33495         /**
33496          * An object containing the default button text strings that can be overriden for localized language support.
33497          * Supported properties are: ok, cancel, yes and no.
33498          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33499          * @type Object
33500          */
33501         buttonText : {
33502             ok : "OK",
33503             cancel : "Cancel",
33504             yes : "Yes",
33505             no : "No"
33506         }
33507     };
33508 }();
33509
33510 /**
33511  * Shorthand for {@link Roo.MessageBox}
33512  */
33513 Roo.Msg = Roo.MessageBox;/*
33514  * Based on:
33515  * Ext JS Library 1.1.1
33516  * Copyright(c) 2006-2007, Ext JS, LLC.
33517  *
33518  * Originally Released Under LGPL - original licence link has changed is not relivant.
33519  *
33520  * Fork - LGPL
33521  * <script type="text/javascript">
33522  */
33523 /**
33524  * @class Roo.QuickTips
33525  * Provides attractive and customizable tooltips for any element.
33526  * @singleton
33527  */
33528 Roo.QuickTips = function(){
33529     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33530     var ce, bd, xy, dd;
33531     var visible = false, disabled = true, inited = false;
33532     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33533     
33534     var onOver = function(e){
33535         if(disabled){
33536             return;
33537         }
33538         var t = e.getTarget();
33539         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33540             return;
33541         }
33542         if(ce && t == ce.el){
33543             clearTimeout(hideProc);
33544             return;
33545         }
33546         if(t && tagEls[t.id]){
33547             tagEls[t.id].el = t;
33548             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33549             return;
33550         }
33551         var ttp, et = Roo.fly(t);
33552         var ns = cfg.namespace;
33553         if(tm.interceptTitles && t.title){
33554             ttp = t.title;
33555             t.qtip = ttp;
33556             t.removeAttribute("title");
33557             e.preventDefault();
33558         }else{
33559             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33560         }
33561         if(ttp){
33562             showProc = show.defer(tm.showDelay, tm, [{
33563                 el: t, 
33564                 text: ttp, 
33565                 width: et.getAttributeNS(ns, cfg.width),
33566                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33567                 title: et.getAttributeNS(ns, cfg.title),
33568                     cls: et.getAttributeNS(ns, cfg.cls)
33569             }]);
33570         }
33571     };
33572     
33573     var onOut = function(e){
33574         clearTimeout(showProc);
33575         var t = e.getTarget();
33576         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33577             hideProc = setTimeout(hide, tm.hideDelay);
33578         }
33579     };
33580     
33581     var onMove = function(e){
33582         if(disabled){
33583             return;
33584         }
33585         xy = e.getXY();
33586         xy[1] += 18;
33587         if(tm.trackMouse && ce){
33588             el.setXY(xy);
33589         }
33590     };
33591     
33592     var onDown = function(e){
33593         clearTimeout(showProc);
33594         clearTimeout(hideProc);
33595         if(!e.within(el)){
33596             if(tm.hideOnClick){
33597                 hide();
33598                 tm.disable();
33599                 tm.enable.defer(100, tm);
33600             }
33601         }
33602     };
33603     
33604     var getPad = function(){
33605         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33606     };
33607
33608     var show = function(o){
33609         if(disabled){
33610             return;
33611         }
33612         clearTimeout(dismissProc);
33613         ce = o;
33614         if(removeCls){ // in case manually hidden
33615             el.removeClass(removeCls);
33616             removeCls = null;
33617         }
33618         if(ce.cls){
33619             el.addClass(ce.cls);
33620             removeCls = ce.cls;
33621         }
33622         if(ce.title){
33623             tipTitle.update(ce.title);
33624             tipTitle.show();
33625         }else{
33626             tipTitle.update('');
33627             tipTitle.hide();
33628         }
33629         el.dom.style.width  = tm.maxWidth+'px';
33630         //tipBody.dom.style.width = '';
33631         tipBodyText.update(o.text);
33632         var p = getPad(), w = ce.width;
33633         if(!w){
33634             var td = tipBodyText.dom;
33635             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33636             if(aw > tm.maxWidth){
33637                 w = tm.maxWidth;
33638             }else if(aw < tm.minWidth){
33639                 w = tm.minWidth;
33640             }else{
33641                 w = aw;
33642             }
33643         }
33644         //tipBody.setWidth(w);
33645         el.setWidth(parseInt(w, 10) + p);
33646         if(ce.autoHide === false){
33647             close.setDisplayed(true);
33648             if(dd){
33649                 dd.unlock();
33650             }
33651         }else{
33652             close.setDisplayed(false);
33653             if(dd){
33654                 dd.lock();
33655             }
33656         }
33657         if(xy){
33658             el.avoidY = xy[1]-18;
33659             el.setXY(xy);
33660         }
33661         if(tm.animate){
33662             el.setOpacity(.1);
33663             el.setStyle("visibility", "visible");
33664             el.fadeIn({callback: afterShow});
33665         }else{
33666             afterShow();
33667         }
33668     };
33669     
33670     var afterShow = function(){
33671         if(ce){
33672             el.show();
33673             esc.enable();
33674             if(tm.autoDismiss && ce.autoHide !== false){
33675                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33676             }
33677         }
33678     };
33679     
33680     var hide = function(noanim){
33681         clearTimeout(dismissProc);
33682         clearTimeout(hideProc);
33683         ce = null;
33684         if(el.isVisible()){
33685             esc.disable();
33686             if(noanim !== true && tm.animate){
33687                 el.fadeOut({callback: afterHide});
33688             }else{
33689                 afterHide();
33690             } 
33691         }
33692     };
33693     
33694     var afterHide = function(){
33695         el.hide();
33696         if(removeCls){
33697             el.removeClass(removeCls);
33698             removeCls = null;
33699         }
33700     };
33701     
33702     return {
33703         /**
33704         * @cfg {Number} minWidth
33705         * The minimum width of the quick tip (defaults to 40)
33706         */
33707        minWidth : 40,
33708         /**
33709         * @cfg {Number} maxWidth
33710         * The maximum width of the quick tip (defaults to 300)
33711         */
33712        maxWidth : 300,
33713         /**
33714         * @cfg {Boolean} interceptTitles
33715         * True to automatically use the element's DOM title value if available (defaults to false)
33716         */
33717        interceptTitles : false,
33718         /**
33719         * @cfg {Boolean} trackMouse
33720         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33721         */
33722        trackMouse : false,
33723         /**
33724         * @cfg {Boolean} hideOnClick
33725         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33726         */
33727        hideOnClick : true,
33728         /**
33729         * @cfg {Number} showDelay
33730         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33731         */
33732        showDelay : 500,
33733         /**
33734         * @cfg {Number} hideDelay
33735         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33736         */
33737        hideDelay : 200,
33738         /**
33739         * @cfg {Boolean} autoHide
33740         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33741         * Used in conjunction with hideDelay.
33742         */
33743        autoHide : true,
33744         /**
33745         * @cfg {Boolean}
33746         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33747         * (defaults to true).  Used in conjunction with autoDismissDelay.
33748         */
33749        autoDismiss : true,
33750         /**
33751         * @cfg {Number}
33752         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33753         */
33754        autoDismissDelay : 5000,
33755        /**
33756         * @cfg {Boolean} animate
33757         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33758         */
33759        animate : false,
33760
33761        /**
33762         * @cfg {String} title
33763         * Title text to display (defaults to '').  This can be any valid HTML markup.
33764         */
33765         title: '',
33766        /**
33767         * @cfg {String} text
33768         * Body text to display (defaults to '').  This can be any valid HTML markup.
33769         */
33770         text : '',
33771        /**
33772         * @cfg {String} cls
33773         * A CSS class to apply to the base quick tip element (defaults to '').
33774         */
33775         cls : '',
33776        /**
33777         * @cfg {Number} width
33778         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33779         * minWidth or maxWidth.
33780         */
33781         width : null,
33782
33783     /**
33784      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33785      * or display QuickTips in a page.
33786      */
33787        init : function(){
33788           tm = Roo.QuickTips;
33789           cfg = tm.tagConfig;
33790           if(!inited){
33791               if(!Roo.isReady){ // allow calling of init() before onReady
33792                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33793                   return;
33794               }
33795               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33796               el.fxDefaults = {stopFx: true};
33797               // maximum custom styling
33798               //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>');
33799               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>');              
33800               tipTitle = el.child('h3');
33801               tipTitle.enableDisplayMode("block");
33802               tipBody = el.child('div.x-tip-bd');
33803               tipBodyText = el.child('div.x-tip-bd-inner');
33804               //bdLeft = el.child('div.x-tip-bd-left');
33805               //bdRight = el.child('div.x-tip-bd-right');
33806               close = el.child('div.x-tip-close');
33807               close.enableDisplayMode("block");
33808               close.on("click", hide);
33809               var d = Roo.get(document);
33810               d.on("mousedown", onDown);
33811               d.on("mouseover", onOver);
33812               d.on("mouseout", onOut);
33813               d.on("mousemove", onMove);
33814               esc = d.addKeyListener(27, hide);
33815               esc.disable();
33816               if(Roo.dd.DD){
33817                   dd = el.initDD("default", null, {
33818                       onDrag : function(){
33819                           el.sync();  
33820                       }
33821                   });
33822                   dd.setHandleElId(tipTitle.id);
33823                   dd.lock();
33824               }
33825               inited = true;
33826           }
33827           this.enable(); 
33828        },
33829
33830     /**
33831      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33832      * are supported:
33833      * <pre>
33834 Property    Type                   Description
33835 ----------  ---------------------  ------------------------------------------------------------------------
33836 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33837      * </ul>
33838      * @param {Object} config The config object
33839      */
33840        register : function(config){
33841            var cs = config instanceof Array ? config : arguments;
33842            for(var i = 0, len = cs.length; i < len; i++) {
33843                var c = cs[i];
33844                var target = c.target;
33845                if(target){
33846                    if(target instanceof Array){
33847                        for(var j = 0, jlen = target.length; j < jlen; j++){
33848                            tagEls[target[j]] = c;
33849                        }
33850                    }else{
33851                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33852                    }
33853                }
33854            }
33855        },
33856
33857     /**
33858      * Removes this quick tip from its element and destroys it.
33859      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33860      */
33861        unregister : function(el){
33862            delete tagEls[Roo.id(el)];
33863        },
33864
33865     /**
33866      * Enable this quick tip.
33867      */
33868        enable : function(){
33869            if(inited && disabled){
33870                locks.pop();
33871                if(locks.length < 1){
33872                    disabled = false;
33873                }
33874            }
33875        },
33876
33877     /**
33878      * Disable this quick tip.
33879      */
33880        disable : function(){
33881           disabled = true;
33882           clearTimeout(showProc);
33883           clearTimeout(hideProc);
33884           clearTimeout(dismissProc);
33885           if(ce){
33886               hide(true);
33887           }
33888           locks.push(1);
33889        },
33890
33891     /**
33892      * Returns true if the quick tip is enabled, else false.
33893      */
33894        isEnabled : function(){
33895             return !disabled;
33896        },
33897
33898         // private
33899        tagConfig : {
33900            namespace : "roo", // was ext?? this may break..
33901            alt_namespace : "ext",
33902            attribute : "qtip",
33903            width : "width",
33904            target : "target",
33905            title : "qtitle",
33906            hide : "hide",
33907            cls : "qclass"
33908        }
33909    };
33910 }();
33911
33912 // backwards compat
33913 Roo.QuickTips.tips = Roo.QuickTips.register;/*
33914  * Based on:
33915  * Ext JS Library 1.1.1
33916  * Copyright(c) 2006-2007, Ext JS, LLC.
33917  *
33918  * Originally Released Under LGPL - original licence link has changed is not relivant.
33919  *
33920  * Fork - LGPL
33921  * <script type="text/javascript">
33922  */
33923  
33924
33925 /**
33926  * @class Roo.tree.TreePanel
33927  * @extends Roo.data.Tree
33928
33929  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
33930  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
33931  * @cfg {Boolean} enableDD true to enable drag and drop
33932  * @cfg {Boolean} enableDrag true to enable just drag
33933  * @cfg {Boolean} enableDrop true to enable just drop
33934  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
33935  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
33936  * @cfg {String} ddGroup The DD group this TreePanel belongs to
33937  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
33938  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
33939  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
33940  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
33941  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
33942  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
33943  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
33944  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
33945  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
33946  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
33947  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
33948  * @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>
33949  * @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>
33950  * 
33951  * @constructor
33952  * @param {String/HTMLElement/Element} el The container element
33953  * @param {Object} config
33954  */
33955 Roo.tree.TreePanel = function(el, config){
33956     var root = false;
33957     var loader = false;
33958     if (config.root) {
33959         root = config.root;
33960         delete config.root;
33961     }
33962     if (config.loader) {
33963         loader = config.loader;
33964         delete config.loader;
33965     }
33966     
33967     Roo.apply(this, config);
33968     Roo.tree.TreePanel.superclass.constructor.call(this);
33969     this.el = Roo.get(el);
33970     this.el.addClass('x-tree');
33971     //console.log(root);
33972     if (root) {
33973         this.setRootNode( Roo.factory(root, Roo.tree));
33974     }
33975     if (loader) {
33976         this.loader = Roo.factory(loader, Roo.tree);
33977     }
33978    /**
33979     * Read-only. The id of the container element becomes this TreePanel's id.
33980     */
33981     this.id = this.el.id;
33982     this.addEvents({
33983         /**
33984         * @event beforeload
33985         * Fires before a node is loaded, return false to cancel
33986         * @param {Node} node The node being loaded
33987         */
33988         "beforeload" : true,
33989         /**
33990         * @event load
33991         * Fires when a node is loaded
33992         * @param {Node} node The node that was loaded
33993         */
33994         "load" : true,
33995         /**
33996         * @event textchange
33997         * Fires when the text for a node is changed
33998         * @param {Node} node The node
33999         * @param {String} text The new text
34000         * @param {String} oldText The old text
34001         */
34002         "textchange" : true,
34003         /**
34004         * @event beforeexpand
34005         * Fires before a node is expanded, return false to cancel.
34006         * @param {Node} node The node
34007         * @param {Boolean} deep
34008         * @param {Boolean} anim
34009         */
34010         "beforeexpand" : true,
34011         /**
34012         * @event beforecollapse
34013         * Fires before a node is collapsed, return false to cancel.
34014         * @param {Node} node The node
34015         * @param {Boolean} deep
34016         * @param {Boolean} anim
34017         */
34018         "beforecollapse" : true,
34019         /**
34020         * @event expand
34021         * Fires when a node is expanded
34022         * @param {Node} node The node
34023         */
34024         "expand" : true,
34025         /**
34026         * @event disabledchange
34027         * Fires when the disabled status of a node changes
34028         * @param {Node} node The node
34029         * @param {Boolean} disabled
34030         */
34031         "disabledchange" : true,
34032         /**
34033         * @event collapse
34034         * Fires when a node is collapsed
34035         * @param {Node} node The node
34036         */
34037         "collapse" : true,
34038         /**
34039         * @event beforeclick
34040         * Fires before click processing on a node. Return false to cancel the default action.
34041         * @param {Node} node The node
34042         * @param {Roo.EventObject} e The event object
34043         */
34044         "beforeclick":true,
34045         /**
34046         * @event checkchange
34047         * Fires when a node with a checkbox's checked property changes
34048         * @param {Node} this This node
34049         * @param {Boolean} checked
34050         */
34051         "checkchange":true,
34052         /**
34053         * @event click
34054         * Fires when a node is clicked
34055         * @param {Node} node The node
34056         * @param {Roo.EventObject} e The event object
34057         */
34058         "click":true,
34059         /**
34060         * @event dblclick
34061         * Fires when a node is double clicked
34062         * @param {Node} node The node
34063         * @param {Roo.EventObject} e The event object
34064         */
34065         "dblclick":true,
34066         /**
34067         * @event contextmenu
34068         * Fires when a node is right clicked
34069         * @param {Node} node The node
34070         * @param {Roo.EventObject} e The event object
34071         */
34072         "contextmenu":true,
34073         /**
34074         * @event beforechildrenrendered
34075         * Fires right before the child nodes for a node are rendered
34076         * @param {Node} node The node
34077         */
34078         "beforechildrenrendered":true,
34079         /**
34080         * @event startdrag
34081         * Fires when a node starts being dragged
34082         * @param {Roo.tree.TreePanel} this
34083         * @param {Roo.tree.TreeNode} node
34084         * @param {event} e The raw browser event
34085         */ 
34086        "startdrag" : true,
34087        /**
34088         * @event enddrag
34089         * Fires when a drag operation is complete
34090         * @param {Roo.tree.TreePanel} this
34091         * @param {Roo.tree.TreeNode} node
34092         * @param {event} e The raw browser event
34093         */
34094        "enddrag" : true,
34095        /**
34096         * @event dragdrop
34097         * Fires when a dragged node is dropped on a valid DD target
34098         * @param {Roo.tree.TreePanel} this
34099         * @param {Roo.tree.TreeNode} node
34100         * @param {DD} dd The dd it was dropped on
34101         * @param {event} e The raw browser event
34102         */
34103        "dragdrop" : true,
34104        /**
34105         * @event beforenodedrop
34106         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34107         * passed to handlers has the following properties:<br />
34108         * <ul style="padding:5px;padding-left:16px;">
34109         * <li>tree - The TreePanel</li>
34110         * <li>target - The node being targeted for the drop</li>
34111         * <li>data - The drag data from the drag source</li>
34112         * <li>point - The point of the drop - append, above or below</li>
34113         * <li>source - The drag source</li>
34114         * <li>rawEvent - Raw mouse event</li>
34115         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34116         * to be inserted by setting them on this object.</li>
34117         * <li>cancel - Set this to true to cancel the drop.</li>
34118         * </ul>
34119         * @param {Object} dropEvent
34120         */
34121        "beforenodedrop" : true,
34122        /**
34123         * @event nodedrop
34124         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34125         * passed to handlers has the following properties:<br />
34126         * <ul style="padding:5px;padding-left:16px;">
34127         * <li>tree - The TreePanel</li>
34128         * <li>target - The node being targeted for the drop</li>
34129         * <li>data - The drag data from the drag source</li>
34130         * <li>point - The point of the drop - append, above or below</li>
34131         * <li>source - The drag source</li>
34132         * <li>rawEvent - Raw mouse event</li>
34133         * <li>dropNode - Dropped node(s).</li>
34134         * </ul>
34135         * @param {Object} dropEvent
34136         */
34137        "nodedrop" : true,
34138         /**
34139         * @event nodedragover
34140         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34141         * passed to handlers has the following properties:<br />
34142         * <ul style="padding:5px;padding-left:16px;">
34143         * <li>tree - The TreePanel</li>
34144         * <li>target - The node being targeted for the drop</li>
34145         * <li>data - The drag data from the drag source</li>
34146         * <li>point - The point of the drop - append, above or below</li>
34147         * <li>source - The drag source</li>
34148         * <li>rawEvent - Raw mouse event</li>
34149         * <li>dropNode - Drop node(s) provided by the source.</li>
34150         * <li>cancel - Set this to true to signal drop not allowed.</li>
34151         * </ul>
34152         * @param {Object} dragOverEvent
34153         */
34154        "nodedragover" : true
34155         
34156     });
34157     if(this.singleExpand){
34158        this.on("beforeexpand", this.restrictExpand, this);
34159     }
34160     if (this.editor) {
34161         this.editor.tree = this;
34162         this.editor = Roo.factory(this.editor, Roo.tree);
34163     }
34164     
34165     if (this.selModel) {
34166         this.selModel = Roo.factory(this.selModel, Roo.tree);
34167     }
34168    
34169 };
34170 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34171     rootVisible : true,
34172     animate: Roo.enableFx,
34173     lines : true,
34174     enableDD : false,
34175     hlDrop : Roo.enableFx,
34176   
34177     renderer: false,
34178     
34179     rendererTip: false,
34180     // private
34181     restrictExpand : function(node){
34182         var p = node.parentNode;
34183         if(p){
34184             if(p.expandedChild && p.expandedChild.parentNode == p){
34185                 p.expandedChild.collapse();
34186             }
34187             p.expandedChild = node;
34188         }
34189     },
34190
34191     // private override
34192     setRootNode : function(node){
34193         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34194         if(!this.rootVisible){
34195             node.ui = new Roo.tree.RootTreeNodeUI(node);
34196         }
34197         return node;
34198     },
34199
34200     /**
34201      * Returns the container element for this TreePanel
34202      */
34203     getEl : function(){
34204         return this.el;
34205     },
34206
34207     /**
34208      * Returns the default TreeLoader for this TreePanel
34209      */
34210     getLoader : function(){
34211         return this.loader;
34212     },
34213
34214     /**
34215      * Expand all nodes
34216      */
34217     expandAll : function(){
34218         this.root.expand(true);
34219     },
34220
34221     /**
34222      * Collapse all nodes
34223      */
34224     collapseAll : function(){
34225         this.root.collapse(true);
34226     },
34227
34228     /**
34229      * Returns the selection model used by this TreePanel
34230      */
34231     getSelectionModel : function(){
34232         if(!this.selModel){
34233             this.selModel = new Roo.tree.DefaultSelectionModel();
34234         }
34235         return this.selModel;
34236     },
34237
34238     /**
34239      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34240      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34241      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34242      * @return {Array}
34243      */
34244     getChecked : function(a, startNode){
34245         startNode = startNode || this.root;
34246         var r = [];
34247         var f = function(){
34248             if(this.attributes.checked){
34249                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34250             }
34251         }
34252         startNode.cascade(f);
34253         return r;
34254     },
34255
34256     /**
34257      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34258      * @param {String} path
34259      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34260      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34261      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34262      */
34263     expandPath : function(path, attr, callback){
34264         attr = attr || "id";
34265         var keys = path.split(this.pathSeparator);
34266         var curNode = this.root;
34267         if(curNode.attributes[attr] != keys[1]){ // invalid root
34268             if(callback){
34269                 callback(false, null);
34270             }
34271             return;
34272         }
34273         var index = 1;
34274         var f = function(){
34275             if(++index == keys.length){
34276                 if(callback){
34277                     callback(true, curNode);
34278                 }
34279                 return;
34280             }
34281             var c = curNode.findChild(attr, keys[index]);
34282             if(!c){
34283                 if(callback){
34284                     callback(false, curNode);
34285                 }
34286                 return;
34287             }
34288             curNode = c;
34289             c.expand(false, false, f);
34290         };
34291         curNode.expand(false, false, f);
34292     },
34293
34294     /**
34295      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34296      * @param {String} path
34297      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34298      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34299      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34300      */
34301     selectPath : function(path, attr, callback){
34302         attr = attr || "id";
34303         var keys = path.split(this.pathSeparator);
34304         var v = keys.pop();
34305         if(keys.length > 0){
34306             var f = function(success, node){
34307                 if(success && node){
34308                     var n = node.findChild(attr, v);
34309                     if(n){
34310                         n.select();
34311                         if(callback){
34312                             callback(true, n);
34313                         }
34314                     }else if(callback){
34315                         callback(false, n);
34316                     }
34317                 }else{
34318                     if(callback){
34319                         callback(false, n);
34320                     }
34321                 }
34322             };
34323             this.expandPath(keys.join(this.pathSeparator), attr, f);
34324         }else{
34325             this.root.select();
34326             if(callback){
34327                 callback(true, this.root);
34328             }
34329         }
34330     },
34331
34332     getTreeEl : function(){
34333         return this.el;
34334     },
34335
34336     /**
34337      * Trigger rendering of this TreePanel
34338      */
34339     render : function(){
34340         if (this.innerCt) {
34341             return this; // stop it rendering more than once!!
34342         }
34343         
34344         this.innerCt = this.el.createChild({tag:"ul",
34345                cls:"x-tree-root-ct " +
34346                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34347
34348         if(this.containerScroll){
34349             Roo.dd.ScrollManager.register(this.el);
34350         }
34351         if((this.enableDD || this.enableDrop) && !this.dropZone){
34352            /**
34353             * The dropZone used by this tree if drop is enabled
34354             * @type Roo.tree.TreeDropZone
34355             */
34356              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34357                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34358            });
34359         }
34360         if((this.enableDD || this.enableDrag) && !this.dragZone){
34361            /**
34362             * The dragZone used by this tree if drag is enabled
34363             * @type Roo.tree.TreeDragZone
34364             */
34365             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34366                ddGroup: this.ddGroup || "TreeDD",
34367                scroll: this.ddScroll
34368            });
34369         }
34370         this.getSelectionModel().init(this);
34371         if (!this.root) {
34372             Roo.log("ROOT not set in tree");
34373             return this;
34374         }
34375         this.root.render();
34376         if(!this.rootVisible){
34377             this.root.renderChildren();
34378         }
34379         return this;
34380     }
34381 });/*
34382  * Based on:
34383  * Ext JS Library 1.1.1
34384  * Copyright(c) 2006-2007, Ext JS, LLC.
34385  *
34386  * Originally Released Under LGPL - original licence link has changed is not relivant.
34387  *
34388  * Fork - LGPL
34389  * <script type="text/javascript">
34390  */
34391  
34392
34393 /**
34394  * @class Roo.tree.DefaultSelectionModel
34395  * @extends Roo.util.Observable
34396  * The default single selection for a TreePanel.
34397  * @param {Object} cfg Configuration
34398  */
34399 Roo.tree.DefaultSelectionModel = function(cfg){
34400    this.selNode = null;
34401    
34402    
34403    
34404    this.addEvents({
34405        /**
34406         * @event selectionchange
34407         * Fires when the selected node changes
34408         * @param {DefaultSelectionModel} this
34409         * @param {TreeNode} node the new selection
34410         */
34411        "selectionchange" : true,
34412
34413        /**
34414         * @event beforeselect
34415         * Fires before the selected node changes, return false to cancel the change
34416         * @param {DefaultSelectionModel} this
34417         * @param {TreeNode} node the new selection
34418         * @param {TreeNode} node the old selection
34419         */
34420        "beforeselect" : true
34421    });
34422    
34423     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34424 };
34425
34426 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34427     init : function(tree){
34428         this.tree = tree;
34429         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34430         tree.on("click", this.onNodeClick, this);
34431     },
34432     
34433     onNodeClick : function(node, e){
34434         if (e.ctrlKey && this.selNode == node)  {
34435             this.unselect(node);
34436             return;
34437         }
34438         this.select(node);
34439     },
34440     
34441     /**
34442      * Select a node.
34443      * @param {TreeNode} node The node to select
34444      * @return {TreeNode} The selected node
34445      */
34446     select : function(node){
34447         var last = this.selNode;
34448         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34449             if(last){
34450                 last.ui.onSelectedChange(false);
34451             }
34452             this.selNode = node;
34453             node.ui.onSelectedChange(true);
34454             this.fireEvent("selectionchange", this, node, last);
34455         }
34456         return node;
34457     },
34458     
34459     /**
34460      * Deselect a node.
34461      * @param {TreeNode} node The node to unselect
34462      */
34463     unselect : function(node){
34464         if(this.selNode == node){
34465             this.clearSelections();
34466         }    
34467     },
34468     
34469     /**
34470      * Clear all selections
34471      */
34472     clearSelections : function(){
34473         var n = this.selNode;
34474         if(n){
34475             n.ui.onSelectedChange(false);
34476             this.selNode = null;
34477             this.fireEvent("selectionchange", this, null);
34478         }
34479         return n;
34480     },
34481     
34482     /**
34483      * Get the selected node
34484      * @return {TreeNode} The selected node
34485      */
34486     getSelectedNode : function(){
34487         return this.selNode;    
34488     },
34489     
34490     /**
34491      * Returns true if the node is selected
34492      * @param {TreeNode} node The node to check
34493      * @return {Boolean}
34494      */
34495     isSelected : function(node){
34496         return this.selNode == node;  
34497     },
34498
34499     /**
34500      * Selects the node above the selected node in the tree, intelligently walking the nodes
34501      * @return TreeNode The new selection
34502      */
34503     selectPrevious : function(){
34504         var s = this.selNode || this.lastSelNode;
34505         if(!s){
34506             return null;
34507         }
34508         var ps = s.previousSibling;
34509         if(ps){
34510             if(!ps.isExpanded() || ps.childNodes.length < 1){
34511                 return this.select(ps);
34512             } else{
34513                 var lc = ps.lastChild;
34514                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34515                     lc = lc.lastChild;
34516                 }
34517                 return this.select(lc);
34518             }
34519         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34520             return this.select(s.parentNode);
34521         }
34522         return null;
34523     },
34524
34525     /**
34526      * Selects the node above the selected node in the tree, intelligently walking the nodes
34527      * @return TreeNode The new selection
34528      */
34529     selectNext : function(){
34530         var s = this.selNode || this.lastSelNode;
34531         if(!s){
34532             return null;
34533         }
34534         if(s.firstChild && s.isExpanded()){
34535              return this.select(s.firstChild);
34536          }else if(s.nextSibling){
34537              return this.select(s.nextSibling);
34538          }else if(s.parentNode){
34539             var newS = null;
34540             s.parentNode.bubble(function(){
34541                 if(this.nextSibling){
34542                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34543                     return false;
34544                 }
34545             });
34546             return newS;
34547          }
34548         return null;
34549     },
34550
34551     onKeyDown : function(e){
34552         var s = this.selNode || this.lastSelNode;
34553         // undesirable, but required
34554         var sm = this;
34555         if(!s){
34556             return;
34557         }
34558         var k = e.getKey();
34559         switch(k){
34560              case e.DOWN:
34561                  e.stopEvent();
34562                  this.selectNext();
34563              break;
34564              case e.UP:
34565                  e.stopEvent();
34566                  this.selectPrevious();
34567              break;
34568              case e.RIGHT:
34569                  e.preventDefault();
34570                  if(s.hasChildNodes()){
34571                      if(!s.isExpanded()){
34572                          s.expand();
34573                      }else if(s.firstChild){
34574                          this.select(s.firstChild, e);
34575                      }
34576                  }
34577              break;
34578              case e.LEFT:
34579                  e.preventDefault();
34580                  if(s.hasChildNodes() && s.isExpanded()){
34581                      s.collapse();
34582                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34583                      this.select(s.parentNode, e);
34584                  }
34585              break;
34586         };
34587     }
34588 });
34589
34590 /**
34591  * @class Roo.tree.MultiSelectionModel
34592  * @extends Roo.util.Observable
34593  * Multi selection for a TreePanel.
34594  * @param {Object} cfg Configuration
34595  */
34596 Roo.tree.MultiSelectionModel = function(){
34597    this.selNodes = [];
34598    this.selMap = {};
34599    this.addEvents({
34600        /**
34601         * @event selectionchange
34602         * Fires when the selected nodes change
34603         * @param {MultiSelectionModel} this
34604         * @param {Array} nodes Array of the selected nodes
34605         */
34606        "selectionchange" : true
34607    });
34608    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34609    
34610 };
34611
34612 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34613     init : function(tree){
34614         this.tree = tree;
34615         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34616         tree.on("click", this.onNodeClick, this);
34617     },
34618     
34619     onNodeClick : function(node, e){
34620         this.select(node, e, e.ctrlKey);
34621     },
34622     
34623     /**
34624      * Select a node.
34625      * @param {TreeNode} node The node to select
34626      * @param {EventObject} e (optional) An event associated with the selection
34627      * @param {Boolean} keepExisting True to retain existing selections
34628      * @return {TreeNode} The selected node
34629      */
34630     select : function(node, e, keepExisting){
34631         if(keepExisting !== true){
34632             this.clearSelections(true);
34633         }
34634         if(this.isSelected(node)){
34635             this.lastSelNode = node;
34636             return node;
34637         }
34638         this.selNodes.push(node);
34639         this.selMap[node.id] = node;
34640         this.lastSelNode = node;
34641         node.ui.onSelectedChange(true);
34642         this.fireEvent("selectionchange", this, this.selNodes);
34643         return node;
34644     },
34645     
34646     /**
34647      * Deselect a node.
34648      * @param {TreeNode} node The node to unselect
34649      */
34650     unselect : function(node){
34651         if(this.selMap[node.id]){
34652             node.ui.onSelectedChange(false);
34653             var sn = this.selNodes;
34654             var index = -1;
34655             if(sn.indexOf){
34656                 index = sn.indexOf(node);
34657             }else{
34658                 for(var i = 0, len = sn.length; i < len; i++){
34659                     if(sn[i] == node){
34660                         index = i;
34661                         break;
34662                     }
34663                 }
34664             }
34665             if(index != -1){
34666                 this.selNodes.splice(index, 1);
34667             }
34668             delete this.selMap[node.id];
34669             this.fireEvent("selectionchange", this, this.selNodes);
34670         }
34671     },
34672     
34673     /**
34674      * Clear all selections
34675      */
34676     clearSelections : function(suppressEvent){
34677         var sn = this.selNodes;
34678         if(sn.length > 0){
34679             for(var i = 0, len = sn.length; i < len; i++){
34680                 sn[i].ui.onSelectedChange(false);
34681             }
34682             this.selNodes = [];
34683             this.selMap = {};
34684             if(suppressEvent !== true){
34685                 this.fireEvent("selectionchange", this, this.selNodes);
34686             }
34687         }
34688     },
34689     
34690     /**
34691      * Returns true if the node is selected
34692      * @param {TreeNode} node The node to check
34693      * @return {Boolean}
34694      */
34695     isSelected : function(node){
34696         return this.selMap[node.id] ? true : false;  
34697     },
34698     
34699     /**
34700      * Returns an array of the selected nodes
34701      * @return {Array}
34702      */
34703     getSelectedNodes : function(){
34704         return this.selNodes;    
34705     },
34706
34707     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34708
34709     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34710
34711     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34712 });/*
34713  * Based on:
34714  * Ext JS Library 1.1.1
34715  * Copyright(c) 2006-2007, Ext JS, LLC.
34716  *
34717  * Originally Released Under LGPL - original licence link has changed is not relivant.
34718  *
34719  * Fork - LGPL
34720  * <script type="text/javascript">
34721  */
34722  
34723 /**
34724  * @class Roo.tree.TreeNode
34725  * @extends Roo.data.Node
34726  * @cfg {String} text The text for this node
34727  * @cfg {Boolean} expanded true to start the node expanded
34728  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34729  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34730  * @cfg {Boolean} disabled true to start the node disabled
34731  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34732  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34733  * @cfg {String} cls A css class to be added to the node
34734  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34735  * @cfg {String} href URL of the link used for the node (defaults to #)
34736  * @cfg {String} hrefTarget target frame for the link
34737  * @cfg {String} qtip An Ext QuickTip for the node
34738  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34739  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34740  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34741  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34742  * (defaults to undefined with no checkbox rendered)
34743  * @constructor
34744  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34745  */
34746 Roo.tree.TreeNode = function(attributes){
34747     attributes = attributes || {};
34748     if(typeof attributes == "string"){
34749         attributes = {text: attributes};
34750     }
34751     this.childrenRendered = false;
34752     this.rendered = false;
34753     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34754     this.expanded = attributes.expanded === true;
34755     this.isTarget = attributes.isTarget !== false;
34756     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34757     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34758
34759     /**
34760      * Read-only. The text for this node. To change it use setText().
34761      * @type String
34762      */
34763     this.text = attributes.text;
34764     /**
34765      * True if this node is disabled.
34766      * @type Boolean
34767      */
34768     this.disabled = attributes.disabled === true;
34769
34770     this.addEvents({
34771         /**
34772         * @event textchange
34773         * Fires when the text for this node is changed
34774         * @param {Node} this This node
34775         * @param {String} text The new text
34776         * @param {String} oldText The old text
34777         */
34778         "textchange" : true,
34779         /**
34780         * @event beforeexpand
34781         * Fires before this node is expanded, return false to cancel.
34782         * @param {Node} this This node
34783         * @param {Boolean} deep
34784         * @param {Boolean} anim
34785         */
34786         "beforeexpand" : true,
34787         /**
34788         * @event beforecollapse
34789         * Fires before this node is collapsed, return false to cancel.
34790         * @param {Node} this This node
34791         * @param {Boolean} deep
34792         * @param {Boolean} anim
34793         */
34794         "beforecollapse" : true,
34795         /**
34796         * @event expand
34797         * Fires when this node is expanded
34798         * @param {Node} this This node
34799         */
34800         "expand" : true,
34801         /**
34802         * @event disabledchange
34803         * Fires when the disabled status of this node changes
34804         * @param {Node} this This node
34805         * @param {Boolean} disabled
34806         */
34807         "disabledchange" : true,
34808         /**
34809         * @event collapse
34810         * Fires when this node is collapsed
34811         * @param {Node} this This node
34812         */
34813         "collapse" : true,
34814         /**
34815         * @event beforeclick
34816         * Fires before click processing. Return false to cancel the default action.
34817         * @param {Node} this This node
34818         * @param {Roo.EventObject} e The event object
34819         */
34820         "beforeclick":true,
34821         /**
34822         * @event checkchange
34823         * Fires when a node with a checkbox's checked property changes
34824         * @param {Node} this This node
34825         * @param {Boolean} checked
34826         */
34827         "checkchange":true,
34828         /**
34829         * @event click
34830         * Fires when this node is clicked
34831         * @param {Node} this This node
34832         * @param {Roo.EventObject} e The event object
34833         */
34834         "click":true,
34835         /**
34836         * @event dblclick
34837         * Fires when this node is double clicked
34838         * @param {Node} this This node
34839         * @param {Roo.EventObject} e The event object
34840         */
34841         "dblclick":true,
34842         /**
34843         * @event contextmenu
34844         * Fires when this node is right clicked
34845         * @param {Node} this This node
34846         * @param {Roo.EventObject} e The event object
34847         */
34848         "contextmenu":true,
34849         /**
34850         * @event beforechildrenrendered
34851         * Fires right before the child nodes for this node are rendered
34852         * @param {Node} this This node
34853         */
34854         "beforechildrenrendered":true
34855     });
34856
34857     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34858
34859     /**
34860      * Read-only. The UI for this node
34861      * @type TreeNodeUI
34862      */
34863     this.ui = new uiClass(this);
34864     
34865     // finally support items[]
34866     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34867         return;
34868     }
34869     
34870     
34871     Roo.each(this.attributes.items, function(c) {
34872         this.appendChild(Roo.factory(c,Roo.Tree));
34873     }, this);
34874     delete this.attributes.items;
34875     
34876     
34877     
34878 };
34879 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34880     preventHScroll: true,
34881     /**
34882      * Returns true if this node is expanded
34883      * @return {Boolean}
34884      */
34885     isExpanded : function(){
34886         return this.expanded;
34887     },
34888
34889     /**
34890      * Returns the UI object for this node
34891      * @return {TreeNodeUI}
34892      */
34893     getUI : function(){
34894         return this.ui;
34895     },
34896
34897     // private override
34898     setFirstChild : function(node){
34899         var of = this.firstChild;
34900         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
34901         if(this.childrenRendered && of && node != of){
34902             of.renderIndent(true, true);
34903         }
34904         if(this.rendered){
34905             this.renderIndent(true, true);
34906         }
34907     },
34908
34909     // private override
34910     setLastChild : function(node){
34911         var ol = this.lastChild;
34912         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
34913         if(this.childrenRendered && ol && node != ol){
34914             ol.renderIndent(true, true);
34915         }
34916         if(this.rendered){
34917             this.renderIndent(true, true);
34918         }
34919     },
34920
34921     // these methods are overridden to provide lazy rendering support
34922     // private override
34923     appendChild : function()
34924     {
34925         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
34926         if(node && this.childrenRendered){
34927             node.render();
34928         }
34929         this.ui.updateExpandIcon();
34930         return node;
34931     },
34932
34933     // private override
34934     removeChild : function(node){
34935         this.ownerTree.getSelectionModel().unselect(node);
34936         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
34937         // if it's been rendered remove dom node
34938         if(this.childrenRendered){
34939             node.ui.remove();
34940         }
34941         if(this.childNodes.length < 1){
34942             this.collapse(false, false);
34943         }else{
34944             this.ui.updateExpandIcon();
34945         }
34946         if(!this.firstChild) {
34947             this.childrenRendered = false;
34948         }
34949         return node;
34950     },
34951
34952     // private override
34953     insertBefore : function(node, refNode){
34954         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
34955         if(newNode && refNode && this.childrenRendered){
34956             node.render();
34957         }
34958         this.ui.updateExpandIcon();
34959         return newNode;
34960     },
34961
34962     /**
34963      * Sets the text for this node
34964      * @param {String} text
34965      */
34966     setText : function(text){
34967         var oldText = this.text;
34968         this.text = text;
34969         this.attributes.text = text;
34970         if(this.rendered){ // event without subscribing
34971             this.ui.onTextChange(this, text, oldText);
34972         }
34973         this.fireEvent("textchange", this, text, oldText);
34974     },
34975
34976     /**
34977      * Triggers selection of this node
34978      */
34979     select : function(){
34980         this.getOwnerTree().getSelectionModel().select(this);
34981     },
34982
34983     /**
34984      * Triggers deselection of this node
34985      */
34986     unselect : function(){
34987         this.getOwnerTree().getSelectionModel().unselect(this);
34988     },
34989
34990     /**
34991      * Returns true if this node is selected
34992      * @return {Boolean}
34993      */
34994     isSelected : function(){
34995         return this.getOwnerTree().getSelectionModel().isSelected(this);
34996     },
34997
34998     /**
34999      * Expand this node.
35000      * @param {Boolean} deep (optional) True to expand all children as well
35001      * @param {Boolean} anim (optional) false to cancel the default animation
35002      * @param {Function} callback (optional) A callback to be called when
35003      * expanding this node completes (does not wait for deep expand to complete).
35004      * Called with 1 parameter, this node.
35005      */
35006     expand : function(deep, anim, callback){
35007         if(!this.expanded){
35008             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35009                 return;
35010             }
35011             if(!this.childrenRendered){
35012                 this.renderChildren();
35013             }
35014             this.expanded = true;
35015             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
35016                 this.ui.animExpand(function(){
35017                     this.fireEvent("expand", this);
35018                     if(typeof callback == "function"){
35019                         callback(this);
35020                     }
35021                     if(deep === true){
35022                         this.expandChildNodes(true);
35023                     }
35024                 }.createDelegate(this));
35025                 return;
35026             }else{
35027                 this.ui.expand();
35028                 this.fireEvent("expand", this);
35029                 if(typeof callback == "function"){
35030                     callback(this);
35031                 }
35032             }
35033         }else{
35034            if(typeof callback == "function"){
35035                callback(this);
35036            }
35037         }
35038         if(deep === true){
35039             this.expandChildNodes(true);
35040         }
35041     },
35042
35043     isHiddenRoot : function(){
35044         return this.isRoot && !this.getOwnerTree().rootVisible;
35045     },
35046
35047     /**
35048      * Collapse this node.
35049      * @param {Boolean} deep (optional) True to collapse all children as well
35050      * @param {Boolean} anim (optional) false to cancel the default animation
35051      */
35052     collapse : function(deep, anim){
35053         if(this.expanded && !this.isHiddenRoot()){
35054             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35055                 return;
35056             }
35057             this.expanded = false;
35058             if((this.getOwnerTree().animate && anim !== false) || anim){
35059                 this.ui.animCollapse(function(){
35060                     this.fireEvent("collapse", this);
35061                     if(deep === true){
35062                         this.collapseChildNodes(true);
35063                     }
35064                 }.createDelegate(this));
35065                 return;
35066             }else{
35067                 this.ui.collapse();
35068                 this.fireEvent("collapse", this);
35069             }
35070         }
35071         if(deep === true){
35072             var cs = this.childNodes;
35073             for(var i = 0, len = cs.length; i < len; i++) {
35074                 cs[i].collapse(true, false);
35075             }
35076         }
35077     },
35078
35079     // private
35080     delayedExpand : function(delay){
35081         if(!this.expandProcId){
35082             this.expandProcId = this.expand.defer(delay, this);
35083         }
35084     },
35085
35086     // private
35087     cancelExpand : function(){
35088         if(this.expandProcId){
35089             clearTimeout(this.expandProcId);
35090         }
35091         this.expandProcId = false;
35092     },
35093
35094     /**
35095      * Toggles expanded/collapsed state of the node
35096      */
35097     toggle : function(){
35098         if(this.expanded){
35099             this.collapse();
35100         }else{
35101             this.expand();
35102         }
35103     },
35104
35105     /**
35106      * Ensures all parent nodes are expanded
35107      */
35108     ensureVisible : function(callback){
35109         var tree = this.getOwnerTree();
35110         tree.expandPath(this.parentNode.getPath(), false, function(){
35111             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35112             Roo.callback(callback);
35113         }.createDelegate(this));
35114     },
35115
35116     /**
35117      * Expand all child nodes
35118      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35119      */
35120     expandChildNodes : function(deep){
35121         var cs = this.childNodes;
35122         for(var i = 0, len = cs.length; i < len; i++) {
35123                 cs[i].expand(deep);
35124         }
35125     },
35126
35127     /**
35128      * Collapse all child nodes
35129      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35130      */
35131     collapseChildNodes : function(deep){
35132         var cs = this.childNodes;
35133         for(var i = 0, len = cs.length; i < len; i++) {
35134                 cs[i].collapse(deep);
35135         }
35136     },
35137
35138     /**
35139      * Disables this node
35140      */
35141     disable : function(){
35142         this.disabled = true;
35143         this.unselect();
35144         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35145             this.ui.onDisableChange(this, true);
35146         }
35147         this.fireEvent("disabledchange", this, true);
35148     },
35149
35150     /**
35151      * Enables this node
35152      */
35153     enable : function(){
35154         this.disabled = false;
35155         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35156             this.ui.onDisableChange(this, false);
35157         }
35158         this.fireEvent("disabledchange", this, false);
35159     },
35160
35161     // private
35162     renderChildren : function(suppressEvent){
35163         if(suppressEvent !== false){
35164             this.fireEvent("beforechildrenrendered", this);
35165         }
35166         var cs = this.childNodes;
35167         for(var i = 0, len = cs.length; i < len; i++){
35168             cs[i].render(true);
35169         }
35170         this.childrenRendered = true;
35171     },
35172
35173     // private
35174     sort : function(fn, scope){
35175         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35176         if(this.childrenRendered){
35177             var cs = this.childNodes;
35178             for(var i = 0, len = cs.length; i < len; i++){
35179                 cs[i].render(true);
35180             }
35181         }
35182     },
35183
35184     // private
35185     render : function(bulkRender){
35186         this.ui.render(bulkRender);
35187         if(!this.rendered){
35188             this.rendered = true;
35189             if(this.expanded){
35190                 this.expanded = false;
35191                 this.expand(false, false);
35192             }
35193         }
35194     },
35195
35196     // private
35197     renderIndent : function(deep, refresh){
35198         if(refresh){
35199             this.ui.childIndent = null;
35200         }
35201         this.ui.renderIndent();
35202         if(deep === true && this.childrenRendered){
35203             var cs = this.childNodes;
35204             for(var i = 0, len = cs.length; i < len; i++){
35205                 cs[i].renderIndent(true, refresh);
35206             }
35207         }
35208     }
35209 });/*
35210  * Based on:
35211  * Ext JS Library 1.1.1
35212  * Copyright(c) 2006-2007, Ext JS, LLC.
35213  *
35214  * Originally Released Under LGPL - original licence link has changed is not relivant.
35215  *
35216  * Fork - LGPL
35217  * <script type="text/javascript">
35218  */
35219  
35220 /**
35221  * @class Roo.tree.AsyncTreeNode
35222  * @extends Roo.tree.TreeNode
35223  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35224  * @constructor
35225  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35226  */
35227  Roo.tree.AsyncTreeNode = function(config){
35228     this.loaded = false;
35229     this.loading = false;
35230     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35231     /**
35232     * @event beforeload
35233     * Fires before this node is loaded, return false to cancel
35234     * @param {Node} this This node
35235     */
35236     this.addEvents({'beforeload':true, 'load': true});
35237     /**
35238     * @event load
35239     * Fires when this node is loaded
35240     * @param {Node} this This node
35241     */
35242     /**
35243      * The loader used by this node (defaults to using the tree's defined loader)
35244      * @type TreeLoader
35245      * @property loader
35246      */
35247 };
35248 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35249     expand : function(deep, anim, callback){
35250         if(this.loading){ // if an async load is already running, waiting til it's done
35251             var timer;
35252             var f = function(){
35253                 if(!this.loading){ // done loading
35254                     clearInterval(timer);
35255                     this.expand(deep, anim, callback);
35256                 }
35257             }.createDelegate(this);
35258             timer = setInterval(f, 200);
35259             return;
35260         }
35261         if(!this.loaded){
35262             if(this.fireEvent("beforeload", this) === false){
35263                 return;
35264             }
35265             this.loading = true;
35266             this.ui.beforeLoad(this);
35267             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35268             if(loader){
35269                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35270                 return;
35271             }
35272         }
35273         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35274     },
35275     
35276     /**
35277      * Returns true if this node is currently loading
35278      * @return {Boolean}
35279      */
35280     isLoading : function(){
35281         return this.loading;  
35282     },
35283     
35284     loadComplete : function(deep, anim, callback){
35285         this.loading = false;
35286         this.loaded = true;
35287         this.ui.afterLoad(this);
35288         this.fireEvent("load", this);
35289         this.expand(deep, anim, callback);
35290     },
35291     
35292     /**
35293      * Returns true if this node has been loaded
35294      * @return {Boolean}
35295      */
35296     isLoaded : function(){
35297         return this.loaded;
35298     },
35299     
35300     hasChildNodes : function(){
35301         if(!this.isLeaf() && !this.loaded){
35302             return true;
35303         }else{
35304             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35305         }
35306     },
35307
35308     /**
35309      * Trigger a reload for this node
35310      * @param {Function} callback
35311      */
35312     reload : function(callback){
35313         this.collapse(false, false);
35314         while(this.firstChild){
35315             this.removeChild(this.firstChild);
35316         }
35317         this.childrenRendered = false;
35318         this.loaded = false;
35319         if(this.isHiddenRoot()){
35320             this.expanded = false;
35321         }
35322         this.expand(false, false, callback);
35323     }
35324 });/*
35325  * Based on:
35326  * Ext JS Library 1.1.1
35327  * Copyright(c) 2006-2007, Ext JS, LLC.
35328  *
35329  * Originally Released Under LGPL - original licence link has changed is not relivant.
35330  *
35331  * Fork - LGPL
35332  * <script type="text/javascript">
35333  */
35334  
35335 /**
35336  * @class Roo.tree.TreeNodeUI
35337  * @constructor
35338  * @param {Object} node The node to render
35339  * The TreeNode UI implementation is separate from the
35340  * tree implementation. Unless you are customizing the tree UI,
35341  * you should never have to use this directly.
35342  */
35343 Roo.tree.TreeNodeUI = function(node){
35344     this.node = node;
35345     this.rendered = false;
35346     this.animating = false;
35347     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35348 };
35349
35350 Roo.tree.TreeNodeUI.prototype = {
35351     removeChild : function(node){
35352         if(this.rendered){
35353             this.ctNode.removeChild(node.ui.getEl());
35354         }
35355     },
35356
35357     beforeLoad : function(){
35358          this.addClass("x-tree-node-loading");
35359     },
35360
35361     afterLoad : function(){
35362          this.removeClass("x-tree-node-loading");
35363     },
35364
35365     onTextChange : function(node, text, oldText){
35366         if(this.rendered){
35367             this.textNode.innerHTML = text;
35368         }
35369     },
35370
35371     onDisableChange : function(node, state){
35372         this.disabled = state;
35373         if(state){
35374             this.addClass("x-tree-node-disabled");
35375         }else{
35376             this.removeClass("x-tree-node-disabled");
35377         }
35378     },
35379
35380     onSelectedChange : function(state){
35381         if(state){
35382             this.focus();
35383             this.addClass("x-tree-selected");
35384         }else{
35385             //this.blur();
35386             this.removeClass("x-tree-selected");
35387         }
35388     },
35389
35390     onMove : function(tree, node, oldParent, newParent, index, refNode){
35391         this.childIndent = null;
35392         if(this.rendered){
35393             var targetNode = newParent.ui.getContainer();
35394             if(!targetNode){//target not rendered
35395                 this.holder = document.createElement("div");
35396                 this.holder.appendChild(this.wrap);
35397                 return;
35398             }
35399             var insertBefore = refNode ? refNode.ui.getEl() : null;
35400             if(insertBefore){
35401                 targetNode.insertBefore(this.wrap, insertBefore);
35402             }else{
35403                 targetNode.appendChild(this.wrap);
35404             }
35405             this.node.renderIndent(true);
35406         }
35407     },
35408
35409     addClass : function(cls){
35410         if(this.elNode){
35411             Roo.fly(this.elNode).addClass(cls);
35412         }
35413     },
35414
35415     removeClass : function(cls){
35416         if(this.elNode){
35417             Roo.fly(this.elNode).removeClass(cls);
35418         }
35419     },
35420
35421     remove : function(){
35422         if(this.rendered){
35423             this.holder = document.createElement("div");
35424             this.holder.appendChild(this.wrap);
35425         }
35426     },
35427
35428     fireEvent : function(){
35429         return this.node.fireEvent.apply(this.node, arguments);
35430     },
35431
35432     initEvents : function(){
35433         this.node.on("move", this.onMove, this);
35434         var E = Roo.EventManager;
35435         var a = this.anchor;
35436
35437         var el = Roo.fly(a, '_treeui');
35438
35439         if(Roo.isOpera){ // opera render bug ignores the CSS
35440             el.setStyle("text-decoration", "none");
35441         }
35442
35443         el.on("click", this.onClick, this);
35444         el.on("dblclick", this.onDblClick, this);
35445
35446         if(this.checkbox){
35447             Roo.EventManager.on(this.checkbox,
35448                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35449         }
35450
35451         el.on("contextmenu", this.onContextMenu, this);
35452
35453         var icon = Roo.fly(this.iconNode);
35454         icon.on("click", this.onClick, this);
35455         icon.on("dblclick", this.onDblClick, this);
35456         icon.on("contextmenu", this.onContextMenu, this);
35457         E.on(this.ecNode, "click", this.ecClick, this, true);
35458
35459         if(this.node.disabled){
35460             this.addClass("x-tree-node-disabled");
35461         }
35462         if(this.node.hidden){
35463             this.addClass("x-tree-node-disabled");
35464         }
35465         var ot = this.node.getOwnerTree();
35466         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35467         if(dd && (!this.node.isRoot || ot.rootVisible)){
35468             Roo.dd.Registry.register(this.elNode, {
35469                 node: this.node,
35470                 handles: this.getDDHandles(),
35471                 isHandle: false
35472             });
35473         }
35474     },
35475
35476     getDDHandles : function(){
35477         return [this.iconNode, this.textNode];
35478     },
35479
35480     hide : function(){
35481         if(this.rendered){
35482             this.wrap.style.display = "none";
35483         }
35484     },
35485
35486     show : function(){
35487         if(this.rendered){
35488             this.wrap.style.display = "";
35489         }
35490     },
35491
35492     onContextMenu : function(e){
35493         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35494             e.preventDefault();
35495             this.focus();
35496             this.fireEvent("contextmenu", this.node, e);
35497         }
35498     },
35499
35500     onClick : function(e){
35501         if(this.dropping){
35502             e.stopEvent();
35503             return;
35504         }
35505         if(this.fireEvent("beforeclick", this.node, e) !== false){
35506             if(!this.disabled && this.node.attributes.href){
35507                 this.fireEvent("click", this.node, e);
35508                 return;
35509             }
35510             e.preventDefault();
35511             if(this.disabled){
35512                 return;
35513             }
35514
35515             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35516                 this.node.toggle();
35517             }
35518
35519             this.fireEvent("click", this.node, e);
35520         }else{
35521             e.stopEvent();
35522         }
35523     },
35524
35525     onDblClick : function(e){
35526         e.preventDefault();
35527         if(this.disabled){
35528             return;
35529         }
35530         if(this.checkbox){
35531             this.toggleCheck();
35532         }
35533         if(!this.animating && this.node.hasChildNodes()){
35534             this.node.toggle();
35535         }
35536         this.fireEvent("dblclick", this.node, e);
35537     },
35538
35539     onCheckChange : function(){
35540         var checked = this.checkbox.checked;
35541         this.node.attributes.checked = checked;
35542         this.fireEvent('checkchange', this.node, checked);
35543     },
35544
35545     ecClick : function(e){
35546         if(!this.animating && this.node.hasChildNodes()){
35547             this.node.toggle();
35548         }
35549     },
35550
35551     startDrop : function(){
35552         this.dropping = true;
35553     },
35554
35555     // delayed drop so the click event doesn't get fired on a drop
35556     endDrop : function(){
35557        setTimeout(function(){
35558            this.dropping = false;
35559        }.createDelegate(this), 50);
35560     },
35561
35562     expand : function(){
35563         this.updateExpandIcon();
35564         this.ctNode.style.display = "";
35565     },
35566
35567     focus : function(){
35568         if(!this.node.preventHScroll){
35569             try{this.anchor.focus();
35570             }catch(e){}
35571         }else if(!Roo.isIE){
35572             try{
35573                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35574                 var l = noscroll.scrollLeft;
35575                 this.anchor.focus();
35576                 noscroll.scrollLeft = l;
35577             }catch(e){}
35578         }
35579     },
35580
35581     toggleCheck : function(value){
35582         var cb = this.checkbox;
35583         if(cb){
35584             cb.checked = (value === undefined ? !cb.checked : value);
35585         }
35586     },
35587
35588     blur : function(){
35589         try{
35590             this.anchor.blur();
35591         }catch(e){}
35592     },
35593
35594     animExpand : function(callback){
35595         var ct = Roo.get(this.ctNode);
35596         ct.stopFx();
35597         if(!this.node.hasChildNodes()){
35598             this.updateExpandIcon();
35599             this.ctNode.style.display = "";
35600             Roo.callback(callback);
35601             return;
35602         }
35603         this.animating = true;
35604         this.updateExpandIcon();
35605
35606         ct.slideIn('t', {
35607            callback : function(){
35608                this.animating = false;
35609                Roo.callback(callback);
35610             },
35611             scope: this,
35612             duration: this.node.ownerTree.duration || .25
35613         });
35614     },
35615
35616     highlight : function(){
35617         var tree = this.node.getOwnerTree();
35618         Roo.fly(this.wrap).highlight(
35619             tree.hlColor || "C3DAF9",
35620             {endColor: tree.hlBaseColor}
35621         );
35622     },
35623
35624     collapse : function(){
35625         this.updateExpandIcon();
35626         this.ctNode.style.display = "none";
35627     },
35628
35629     animCollapse : function(callback){
35630         var ct = Roo.get(this.ctNode);
35631         ct.enableDisplayMode('block');
35632         ct.stopFx();
35633
35634         this.animating = true;
35635         this.updateExpandIcon();
35636
35637         ct.slideOut('t', {
35638             callback : function(){
35639                this.animating = false;
35640                Roo.callback(callback);
35641             },
35642             scope: this,
35643             duration: this.node.ownerTree.duration || .25
35644         });
35645     },
35646
35647     getContainer : function(){
35648         return this.ctNode;
35649     },
35650
35651     getEl : function(){
35652         return this.wrap;
35653     },
35654
35655     appendDDGhost : function(ghostNode){
35656         ghostNode.appendChild(this.elNode.cloneNode(true));
35657     },
35658
35659     getDDRepairXY : function(){
35660         return Roo.lib.Dom.getXY(this.iconNode);
35661     },
35662
35663     onRender : function(){
35664         this.render();
35665     },
35666
35667     render : function(bulkRender){
35668         var n = this.node, a = n.attributes;
35669         var targetNode = n.parentNode ?
35670               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35671
35672         if(!this.rendered){
35673             this.rendered = true;
35674
35675             this.renderElements(n, a, targetNode, bulkRender);
35676
35677             if(a.qtip){
35678                if(this.textNode.setAttributeNS){
35679                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35680                    if(a.qtipTitle){
35681                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35682                    }
35683                }else{
35684                    this.textNode.setAttribute("ext:qtip", a.qtip);
35685                    if(a.qtipTitle){
35686                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35687                    }
35688                }
35689             }else if(a.qtipCfg){
35690                 a.qtipCfg.target = Roo.id(this.textNode);
35691                 Roo.QuickTips.register(a.qtipCfg);
35692             }
35693             this.initEvents();
35694             if(!this.node.expanded){
35695                 this.updateExpandIcon();
35696             }
35697         }else{
35698             if(bulkRender === true) {
35699                 targetNode.appendChild(this.wrap);
35700             }
35701         }
35702     },
35703
35704     renderElements : function(n, a, targetNode, bulkRender)
35705     {
35706         // add some indent caching, this helps performance when rendering a large tree
35707         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35708         var t = n.getOwnerTree();
35709         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35710         if (typeof(n.attributes.html) != 'undefined') {
35711             txt = n.attributes.html;
35712         }
35713         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35714         var cb = typeof a.checked == 'boolean';
35715         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35716         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35717             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35718             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35719             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35720             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35721             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35722              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35723                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35724             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35725             "</li>"];
35726
35727         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35728             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35729                                 n.nextSibling.ui.getEl(), buf.join(""));
35730         }else{
35731             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35732         }
35733
35734         this.elNode = this.wrap.childNodes[0];
35735         this.ctNode = this.wrap.childNodes[1];
35736         var cs = this.elNode.childNodes;
35737         this.indentNode = cs[0];
35738         this.ecNode = cs[1];
35739         this.iconNode = cs[2];
35740         var index = 3;
35741         if(cb){
35742             this.checkbox = cs[3];
35743             index++;
35744         }
35745         this.anchor = cs[index];
35746         this.textNode = cs[index].firstChild;
35747     },
35748
35749     getAnchor : function(){
35750         return this.anchor;
35751     },
35752
35753     getTextEl : function(){
35754         return this.textNode;
35755     },
35756
35757     getIconEl : function(){
35758         return this.iconNode;
35759     },
35760
35761     isChecked : function(){
35762         return this.checkbox ? this.checkbox.checked : false;
35763     },
35764
35765     updateExpandIcon : function(){
35766         if(this.rendered){
35767             var n = this.node, c1, c2;
35768             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35769             var hasChild = n.hasChildNodes();
35770             if(hasChild){
35771                 if(n.expanded){
35772                     cls += "-minus";
35773                     c1 = "x-tree-node-collapsed";
35774                     c2 = "x-tree-node-expanded";
35775                 }else{
35776                     cls += "-plus";
35777                     c1 = "x-tree-node-expanded";
35778                     c2 = "x-tree-node-collapsed";
35779                 }
35780                 if(this.wasLeaf){
35781                     this.removeClass("x-tree-node-leaf");
35782                     this.wasLeaf = false;
35783                 }
35784                 if(this.c1 != c1 || this.c2 != c2){
35785                     Roo.fly(this.elNode).replaceClass(c1, c2);
35786                     this.c1 = c1; this.c2 = c2;
35787                 }
35788             }else{
35789                 // this changes non-leafs into leafs if they have no children.
35790                 // it's not very rational behaviour..
35791                 
35792                 if(!this.wasLeaf && this.node.leaf){
35793                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35794                     delete this.c1;
35795                     delete this.c2;
35796                     this.wasLeaf = true;
35797                 }
35798             }
35799             var ecc = "x-tree-ec-icon "+cls;
35800             if(this.ecc != ecc){
35801                 this.ecNode.className = ecc;
35802                 this.ecc = ecc;
35803             }
35804         }
35805     },
35806
35807     getChildIndent : function(){
35808         if(!this.childIndent){
35809             var buf = [];
35810             var p = this.node;
35811             while(p){
35812                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35813                     if(!p.isLast()) {
35814                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35815                     } else {
35816                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35817                     }
35818                 }
35819                 p = p.parentNode;
35820             }
35821             this.childIndent = buf.join("");
35822         }
35823         return this.childIndent;
35824     },
35825
35826     renderIndent : function(){
35827         if(this.rendered){
35828             var indent = "";
35829             var p = this.node.parentNode;
35830             if(p){
35831                 indent = p.ui.getChildIndent();
35832             }
35833             if(this.indentMarkup != indent){ // don't rerender if not required
35834                 this.indentNode.innerHTML = indent;
35835                 this.indentMarkup = indent;
35836             }
35837             this.updateExpandIcon();
35838         }
35839     }
35840 };
35841
35842 Roo.tree.RootTreeNodeUI = function(){
35843     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35844 };
35845 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35846     render : function(){
35847         if(!this.rendered){
35848             var targetNode = this.node.ownerTree.innerCt.dom;
35849             this.node.expanded = true;
35850             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35851             this.wrap = this.ctNode = targetNode.firstChild;
35852         }
35853     },
35854     collapse : function(){
35855     },
35856     expand : function(){
35857     }
35858 });/*
35859  * Based on:
35860  * Ext JS Library 1.1.1
35861  * Copyright(c) 2006-2007, Ext JS, LLC.
35862  *
35863  * Originally Released Under LGPL - original licence link has changed is not relivant.
35864  *
35865  * Fork - LGPL
35866  * <script type="text/javascript">
35867  */
35868 /**
35869  * @class Roo.tree.TreeLoader
35870  * @extends Roo.util.Observable
35871  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35872  * nodes from a specified URL. The response must be a javascript Array definition
35873  * who's elements are node definition objects. eg:
35874  * <pre><code>
35875 {  success : true,
35876    data :      [
35877    
35878     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35879     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35880     ]
35881 }
35882
35883
35884 </code></pre>
35885  * <br><br>
35886  * The old style respose with just an array is still supported, but not recommended.
35887  * <br><br>
35888  *
35889  * A server request is sent, and child nodes are loaded only when a node is expanded.
35890  * The loading node's id is passed to the server under the parameter name "node" to
35891  * enable the server to produce the correct child nodes.
35892  * <br><br>
35893  * To pass extra parameters, an event handler may be attached to the "beforeload"
35894  * event, and the parameters specified in the TreeLoader's baseParams property:
35895  * <pre><code>
35896     myTreeLoader.on("beforeload", function(treeLoader, node) {
35897         this.baseParams.category = node.attributes.category;
35898     }, this);
35899 </code></pre><
35900  * This would pass an HTTP parameter called "category" to the server containing
35901  * the value of the Node's "category" attribute.
35902  * @constructor
35903  * Creates a new Treeloader.
35904  * @param {Object} config A config object containing config properties.
35905  */
35906 Roo.tree.TreeLoader = function(config){
35907     this.baseParams = {};
35908     this.requestMethod = "POST";
35909     Roo.apply(this, config);
35910
35911     this.addEvents({
35912     
35913         /**
35914          * @event beforeload
35915          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
35916          * @param {Object} This TreeLoader object.
35917          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35918          * @param {Object} callback The callback function specified in the {@link #load} call.
35919          */
35920         beforeload : true,
35921         /**
35922          * @event load
35923          * Fires when the node has been successfuly loaded.
35924          * @param {Object} This TreeLoader object.
35925          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35926          * @param {Object} response The response object containing the data from the server.
35927          */
35928         load : true,
35929         /**
35930          * @event loadexception
35931          * Fires if the network request failed.
35932          * @param {Object} This TreeLoader object.
35933          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35934          * @param {Object} response The response object containing the data from the server.
35935          */
35936         loadexception : true,
35937         /**
35938          * @event create
35939          * Fires before a node is created, enabling you to return custom Node types 
35940          * @param {Object} This TreeLoader object.
35941          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
35942          */
35943         create : true
35944     });
35945
35946     Roo.tree.TreeLoader.superclass.constructor.call(this);
35947 };
35948
35949 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
35950     /**
35951     * @cfg {String} dataUrl The URL from which to request a Json string which
35952     * specifies an array of node definition object representing the child nodes
35953     * to be loaded.
35954     */
35955     /**
35956     * @cfg {String} requestMethod either GET or POST
35957     * defaults to POST (due to BC)
35958     * to be loaded.
35959     */
35960     /**
35961     * @cfg {Object} baseParams (optional) An object containing properties which
35962     * specify HTTP parameters to be passed to each request for child nodes.
35963     */
35964     /**
35965     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
35966     * created by this loader. If the attributes sent by the server have an attribute in this object,
35967     * they take priority.
35968     */
35969     /**
35970     * @cfg {Object} uiProviders (optional) An object containing properties which
35971     * 
35972     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
35973     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
35974     * <i>uiProvider</i> attribute of a returned child node is a string rather
35975     * than a reference to a TreeNodeUI implementation, this that string value
35976     * is used as a property name in the uiProviders object. You can define the provider named
35977     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
35978     */
35979     uiProviders : {},
35980
35981     /**
35982     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
35983     * child nodes before loading.
35984     */
35985     clearOnLoad : true,
35986
35987     /**
35988     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
35989     * property on loading, rather than expecting an array. (eg. more compatible to a standard
35990     * Grid query { data : [ .....] }
35991     */
35992     
35993     root : false,
35994      /**
35995     * @cfg {String} queryParam (optional) 
35996     * Name of the query as it will be passed on the querystring (defaults to 'node')
35997     * eg. the request will be ?node=[id]
35998     */
35999     
36000     
36001     queryParam: false,
36002     
36003     /**
36004      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36005      * This is called automatically when a node is expanded, but may be used to reload
36006      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36007      * @param {Roo.tree.TreeNode} node
36008      * @param {Function} callback
36009      */
36010     load : function(node, callback){
36011         if(this.clearOnLoad){
36012             while(node.firstChild){
36013                 node.removeChild(node.firstChild);
36014             }
36015         }
36016         if(node.attributes.children){ // preloaded json children
36017             var cs = node.attributes.children;
36018             for(var i = 0, len = cs.length; i < len; i++){
36019                 node.appendChild(this.createNode(cs[i]));
36020             }
36021             if(typeof callback == "function"){
36022                 callback();
36023             }
36024         }else if(this.dataUrl){
36025             this.requestData(node, callback);
36026         }
36027     },
36028
36029     getParams: function(node){
36030         var buf = [], bp = this.baseParams;
36031         for(var key in bp){
36032             if(typeof bp[key] != "function"){
36033                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36034             }
36035         }
36036         var n = this.queryParam === false ? 'node' : this.queryParam;
36037         buf.push(n + "=", encodeURIComponent(node.id));
36038         return buf.join("");
36039     },
36040
36041     requestData : function(node, callback){
36042         if(this.fireEvent("beforeload", this, node, callback) !== false){
36043             this.transId = Roo.Ajax.request({
36044                 method:this.requestMethod,
36045                 url: this.dataUrl||this.url,
36046                 success: this.handleResponse,
36047                 failure: this.handleFailure,
36048                 scope: this,
36049                 argument: {callback: callback, node: node},
36050                 params: this.getParams(node)
36051             });
36052         }else{
36053             // if the load is cancelled, make sure we notify
36054             // the node that we are done
36055             if(typeof callback == "function"){
36056                 callback();
36057             }
36058         }
36059     },
36060
36061     isLoading : function(){
36062         return this.transId ? true : false;
36063     },
36064
36065     abort : function(){
36066         if(this.isLoading()){
36067             Roo.Ajax.abort(this.transId);
36068         }
36069     },
36070
36071     // private
36072     createNode : function(attr)
36073     {
36074         // apply baseAttrs, nice idea Corey!
36075         if(this.baseAttrs){
36076             Roo.applyIf(attr, this.baseAttrs);
36077         }
36078         if(this.applyLoader !== false){
36079             attr.loader = this;
36080         }
36081         // uiProvider = depreciated..
36082         
36083         if(typeof(attr.uiProvider) == 'string'){
36084            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36085                 /**  eval:var:attr */ eval(attr.uiProvider);
36086         }
36087         if(typeof(this.uiProviders['default']) != 'undefined') {
36088             attr.uiProvider = this.uiProviders['default'];
36089         }
36090         
36091         this.fireEvent('create', this, attr);
36092         
36093         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36094         return(attr.leaf ?
36095                         new Roo.tree.TreeNode(attr) :
36096                         new Roo.tree.AsyncTreeNode(attr));
36097     },
36098
36099     processResponse : function(response, node, callback)
36100     {
36101         var json = response.responseText;
36102         try {
36103             
36104             var o = Roo.decode(json);
36105             
36106             if (this.root === false && typeof(o.success) != undefined) {
36107                 this.root = 'data'; // the default behaviour for list like data..
36108                 }
36109                 
36110             if (this.root !== false &&  !o.success) {
36111                 // it's a failure condition.
36112                 var a = response.argument;
36113                 this.fireEvent("loadexception", this, a.node, response);
36114                 Roo.log("Load failed - should have a handler really");
36115                 return;
36116             }
36117             
36118             
36119             
36120             if (this.root !== false) {
36121                  o = o[this.root];
36122             }
36123             
36124             for(var i = 0, len = o.length; i < len; i++){
36125                 var n = this.createNode(o[i]);
36126                 if(n){
36127                     node.appendChild(n);
36128                 }
36129             }
36130             if(typeof callback == "function"){
36131                 callback(this, node);
36132             }
36133         }catch(e){
36134             this.handleFailure(response);
36135         }
36136     },
36137
36138     handleResponse : function(response){
36139         this.transId = false;
36140         var a = response.argument;
36141         this.processResponse(response, a.node, a.callback);
36142         this.fireEvent("load", this, a.node, response);
36143     },
36144
36145     handleFailure : function(response)
36146     {
36147         // should handle failure better..
36148         this.transId = false;
36149         var a = response.argument;
36150         this.fireEvent("loadexception", this, a.node, response);
36151         if(typeof a.callback == "function"){
36152             a.callback(this, a.node);
36153         }
36154     }
36155 });/*
36156  * Based on:
36157  * Ext JS Library 1.1.1
36158  * Copyright(c) 2006-2007, Ext JS, LLC.
36159  *
36160  * Originally Released Under LGPL - original licence link has changed is not relivant.
36161  *
36162  * Fork - LGPL
36163  * <script type="text/javascript">
36164  */
36165
36166 /**
36167 * @class Roo.tree.TreeFilter
36168 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36169 * @param {TreePanel} tree
36170 * @param {Object} config (optional)
36171  */
36172 Roo.tree.TreeFilter = function(tree, config){
36173     this.tree = tree;
36174     this.filtered = {};
36175     Roo.apply(this, config);
36176 };
36177
36178 Roo.tree.TreeFilter.prototype = {
36179     clearBlank:false,
36180     reverse:false,
36181     autoClear:false,
36182     remove:false,
36183
36184      /**
36185      * Filter the data by a specific attribute.
36186      * @param {String/RegExp} value Either string that the attribute value
36187      * should start with or a RegExp to test against the attribute
36188      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36189      * @param {TreeNode} startNode (optional) The node to start the filter at.
36190      */
36191     filter : function(value, attr, startNode){
36192         attr = attr || "text";
36193         var f;
36194         if(typeof value == "string"){
36195             var vlen = value.length;
36196             // auto clear empty filter
36197             if(vlen == 0 && this.clearBlank){
36198                 this.clear();
36199                 return;
36200             }
36201             value = value.toLowerCase();
36202             f = function(n){
36203                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36204             };
36205         }else if(value.exec){ // regex?
36206             f = function(n){
36207                 return value.test(n.attributes[attr]);
36208             };
36209         }else{
36210             throw 'Illegal filter type, must be string or regex';
36211         }
36212         this.filterBy(f, null, startNode);
36213         },
36214
36215     /**
36216      * Filter by a function. The passed function will be called with each
36217      * node in the tree (or from the startNode). If the function returns true, the node is kept
36218      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36219      * @param {Function} fn The filter function
36220      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36221      */
36222     filterBy : function(fn, scope, startNode){
36223         startNode = startNode || this.tree.root;
36224         if(this.autoClear){
36225             this.clear();
36226         }
36227         var af = this.filtered, rv = this.reverse;
36228         var f = function(n){
36229             if(n == startNode){
36230                 return true;
36231             }
36232             if(af[n.id]){
36233                 return false;
36234             }
36235             var m = fn.call(scope || n, n);
36236             if(!m || rv){
36237                 af[n.id] = n;
36238                 n.ui.hide();
36239                 return false;
36240             }
36241             return true;
36242         };
36243         startNode.cascade(f);
36244         if(this.remove){
36245            for(var id in af){
36246                if(typeof id != "function"){
36247                    var n = af[id];
36248                    if(n && n.parentNode){
36249                        n.parentNode.removeChild(n);
36250                    }
36251                }
36252            }
36253         }
36254     },
36255
36256     /**
36257      * Clears the current filter. Note: with the "remove" option
36258      * set a filter cannot be cleared.
36259      */
36260     clear : function(){
36261         var t = this.tree;
36262         var af = this.filtered;
36263         for(var id in af){
36264             if(typeof id != "function"){
36265                 var n = af[id];
36266                 if(n){
36267                     n.ui.show();
36268                 }
36269             }
36270         }
36271         this.filtered = {};
36272     }
36273 };
36274 /*
36275  * Based on:
36276  * Ext JS Library 1.1.1
36277  * Copyright(c) 2006-2007, Ext JS, LLC.
36278  *
36279  * Originally Released Under LGPL - original licence link has changed is not relivant.
36280  *
36281  * Fork - LGPL
36282  * <script type="text/javascript">
36283  */
36284  
36285
36286 /**
36287  * @class Roo.tree.TreeSorter
36288  * Provides sorting of nodes in a TreePanel
36289  * 
36290  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36291  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36292  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36293  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36294  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36295  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36296  * @constructor
36297  * @param {TreePanel} tree
36298  * @param {Object} config
36299  */
36300 Roo.tree.TreeSorter = function(tree, config){
36301     Roo.apply(this, config);
36302     tree.on("beforechildrenrendered", this.doSort, this);
36303     tree.on("append", this.updateSort, this);
36304     tree.on("insert", this.updateSort, this);
36305     
36306     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36307     var p = this.property || "text";
36308     var sortType = this.sortType;
36309     var fs = this.folderSort;
36310     var cs = this.caseSensitive === true;
36311     var leafAttr = this.leafAttr || 'leaf';
36312
36313     this.sortFn = function(n1, n2){
36314         if(fs){
36315             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36316                 return 1;
36317             }
36318             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36319                 return -1;
36320             }
36321         }
36322         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36323         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36324         if(v1 < v2){
36325                         return dsc ? +1 : -1;
36326                 }else if(v1 > v2){
36327                         return dsc ? -1 : +1;
36328         }else{
36329                 return 0;
36330         }
36331     };
36332 };
36333
36334 Roo.tree.TreeSorter.prototype = {
36335     doSort : function(node){
36336         node.sort(this.sortFn);
36337     },
36338     
36339     compareNodes : function(n1, n2){
36340         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36341     },
36342     
36343     updateSort : function(tree, node){
36344         if(node.childrenRendered){
36345             this.doSort.defer(1, this, [node]);
36346         }
36347     }
36348 };/*
36349  * Based on:
36350  * Ext JS Library 1.1.1
36351  * Copyright(c) 2006-2007, Ext JS, LLC.
36352  *
36353  * Originally Released Under LGPL - original licence link has changed is not relivant.
36354  *
36355  * Fork - LGPL
36356  * <script type="text/javascript">
36357  */
36358
36359 if(Roo.dd.DropZone){
36360     
36361 Roo.tree.TreeDropZone = function(tree, config){
36362     this.allowParentInsert = false;
36363     this.allowContainerDrop = false;
36364     this.appendOnly = false;
36365     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36366     this.tree = tree;
36367     this.lastInsertClass = "x-tree-no-status";
36368     this.dragOverData = {};
36369 };
36370
36371 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36372     ddGroup : "TreeDD",
36373     scroll:  true,
36374     
36375     expandDelay : 1000,
36376     
36377     expandNode : function(node){
36378         if(node.hasChildNodes() && !node.isExpanded()){
36379             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36380         }
36381     },
36382     
36383     queueExpand : function(node){
36384         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36385     },
36386     
36387     cancelExpand : function(){
36388         if(this.expandProcId){
36389             clearTimeout(this.expandProcId);
36390             this.expandProcId = false;
36391         }
36392     },
36393     
36394     isValidDropPoint : function(n, pt, dd, e, data){
36395         if(!n || !data){ return false; }
36396         var targetNode = n.node;
36397         var dropNode = data.node;
36398         // default drop rules
36399         if(!(targetNode && targetNode.isTarget && pt)){
36400             return false;
36401         }
36402         if(pt == "append" && targetNode.allowChildren === false){
36403             return false;
36404         }
36405         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36406             return false;
36407         }
36408         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36409             return false;
36410         }
36411         // reuse the object
36412         var overEvent = this.dragOverData;
36413         overEvent.tree = this.tree;
36414         overEvent.target = targetNode;
36415         overEvent.data = data;
36416         overEvent.point = pt;
36417         overEvent.source = dd;
36418         overEvent.rawEvent = e;
36419         overEvent.dropNode = dropNode;
36420         overEvent.cancel = false;  
36421         var result = this.tree.fireEvent("nodedragover", overEvent);
36422         return overEvent.cancel === false && result !== false;
36423     },
36424     
36425     getDropPoint : function(e, n, dd)
36426     {
36427         var tn = n.node;
36428         if(tn.isRoot){
36429             return tn.allowChildren !== false ? "append" : false; // always append for root
36430         }
36431         var dragEl = n.ddel;
36432         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36433         var y = Roo.lib.Event.getPageY(e);
36434         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36435         
36436         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36437         var noAppend = tn.allowChildren === false;
36438         if(this.appendOnly || tn.parentNode.allowChildren === false){
36439             return noAppend ? false : "append";
36440         }
36441         var noBelow = false;
36442         if(!this.allowParentInsert){
36443             noBelow = tn.hasChildNodes() && tn.isExpanded();
36444         }
36445         var q = (b - t) / (noAppend ? 2 : 3);
36446         if(y >= t && y < (t + q)){
36447             return "above";
36448         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36449             return "below";
36450         }else{
36451             return "append";
36452         }
36453     },
36454     
36455     onNodeEnter : function(n, dd, e, data)
36456     {
36457         this.cancelExpand();
36458     },
36459     
36460     onNodeOver : function(n, dd, e, data)
36461     {
36462        
36463         var pt = this.getDropPoint(e, n, dd);
36464         var node = n.node;
36465         
36466         // auto node expand check
36467         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36468             this.queueExpand(node);
36469         }else if(pt != "append"){
36470             this.cancelExpand();
36471         }
36472         
36473         // set the insert point style on the target node
36474         var returnCls = this.dropNotAllowed;
36475         if(this.isValidDropPoint(n, pt, dd, e, data)){
36476            if(pt){
36477                var el = n.ddel;
36478                var cls;
36479                if(pt == "above"){
36480                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36481                    cls = "x-tree-drag-insert-above";
36482                }else if(pt == "below"){
36483                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36484                    cls = "x-tree-drag-insert-below";
36485                }else{
36486                    returnCls = "x-tree-drop-ok-append";
36487                    cls = "x-tree-drag-append";
36488                }
36489                if(this.lastInsertClass != cls){
36490                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36491                    this.lastInsertClass = cls;
36492                }
36493            }
36494        }
36495        return returnCls;
36496     },
36497     
36498     onNodeOut : function(n, dd, e, data){
36499         
36500         this.cancelExpand();
36501         this.removeDropIndicators(n);
36502     },
36503     
36504     onNodeDrop : function(n, dd, e, data){
36505         var point = this.getDropPoint(e, n, dd);
36506         var targetNode = n.node;
36507         targetNode.ui.startDrop();
36508         if(!this.isValidDropPoint(n, point, dd, e, data)){
36509             targetNode.ui.endDrop();
36510             return false;
36511         }
36512         // first try to find the drop node
36513         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36514         var dropEvent = {
36515             tree : this.tree,
36516             target: targetNode,
36517             data: data,
36518             point: point,
36519             source: dd,
36520             rawEvent: e,
36521             dropNode: dropNode,
36522             cancel: !dropNode   
36523         };
36524         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36525         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36526             targetNode.ui.endDrop();
36527             return false;
36528         }
36529         // allow target changing
36530         targetNode = dropEvent.target;
36531         if(point == "append" && !targetNode.isExpanded()){
36532             targetNode.expand(false, null, function(){
36533                 this.completeDrop(dropEvent);
36534             }.createDelegate(this));
36535         }else{
36536             this.completeDrop(dropEvent);
36537         }
36538         return true;
36539     },
36540     
36541     completeDrop : function(de){
36542         var ns = de.dropNode, p = de.point, t = de.target;
36543         if(!(ns instanceof Array)){
36544             ns = [ns];
36545         }
36546         var n;
36547         for(var i = 0, len = ns.length; i < len; i++){
36548             n = ns[i];
36549             if(p == "above"){
36550                 t.parentNode.insertBefore(n, t);
36551             }else if(p == "below"){
36552                 t.parentNode.insertBefore(n, t.nextSibling);
36553             }else{
36554                 t.appendChild(n);
36555             }
36556         }
36557         n.ui.focus();
36558         if(this.tree.hlDrop){
36559             n.ui.highlight();
36560         }
36561         t.ui.endDrop();
36562         this.tree.fireEvent("nodedrop", de);
36563     },
36564     
36565     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36566         if(this.tree.hlDrop){
36567             dropNode.ui.focus();
36568             dropNode.ui.highlight();
36569         }
36570         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36571     },
36572     
36573     getTree : function(){
36574         return this.tree;
36575     },
36576     
36577     removeDropIndicators : function(n){
36578         if(n && n.ddel){
36579             var el = n.ddel;
36580             Roo.fly(el).removeClass([
36581                     "x-tree-drag-insert-above",
36582                     "x-tree-drag-insert-below",
36583                     "x-tree-drag-append"]);
36584             this.lastInsertClass = "_noclass";
36585         }
36586     },
36587     
36588     beforeDragDrop : function(target, e, id){
36589         this.cancelExpand();
36590         return true;
36591     },
36592     
36593     afterRepair : function(data){
36594         if(data && Roo.enableFx){
36595             data.node.ui.highlight();
36596         }
36597         this.hideProxy();
36598     } 
36599     
36600 });
36601
36602 }
36603 /*
36604  * Based on:
36605  * Ext JS Library 1.1.1
36606  * Copyright(c) 2006-2007, Ext JS, LLC.
36607  *
36608  * Originally Released Under LGPL - original licence link has changed is not relivant.
36609  *
36610  * Fork - LGPL
36611  * <script type="text/javascript">
36612  */
36613  
36614
36615 if(Roo.dd.DragZone){
36616 Roo.tree.TreeDragZone = function(tree, config){
36617     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36618     this.tree = tree;
36619 };
36620
36621 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36622     ddGroup : "TreeDD",
36623    
36624     onBeforeDrag : function(data, e){
36625         var n = data.node;
36626         return n && n.draggable && !n.disabled;
36627     },
36628      
36629     
36630     onInitDrag : function(e){
36631         var data = this.dragData;
36632         this.tree.getSelectionModel().select(data.node);
36633         this.proxy.update("");
36634         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36635         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36636     },
36637     
36638     getRepairXY : function(e, data){
36639         return data.node.ui.getDDRepairXY();
36640     },
36641     
36642     onEndDrag : function(data, e){
36643         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36644         
36645         
36646     },
36647     
36648     onValidDrop : function(dd, e, id){
36649         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36650         this.hideProxy();
36651     },
36652     
36653     beforeInvalidDrop : function(e, id){
36654         // this scrolls the original position back into view
36655         var sm = this.tree.getSelectionModel();
36656         sm.clearSelections();
36657         sm.select(this.dragData.node);
36658     }
36659 });
36660 }/*
36661  * Based on:
36662  * Ext JS Library 1.1.1
36663  * Copyright(c) 2006-2007, Ext JS, LLC.
36664  *
36665  * Originally Released Under LGPL - original licence link has changed is not relivant.
36666  *
36667  * Fork - LGPL
36668  * <script type="text/javascript">
36669  */
36670 /**
36671  * @class Roo.tree.TreeEditor
36672  * @extends Roo.Editor
36673  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36674  * as the editor field.
36675  * @constructor
36676  * @param {Object} config (used to be the tree panel.)
36677  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36678  * 
36679  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36680  * @cfg {Roo.form.TextField|Object} field The field configuration
36681  *
36682  * 
36683  */
36684 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36685     var tree = config;
36686     var field;
36687     if (oldconfig) { // old style..
36688         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36689     } else {
36690         // new style..
36691         tree = config.tree;
36692         config.field = config.field  || {};
36693         config.field.xtype = 'TextField';
36694         field = Roo.factory(config.field, Roo.form);
36695     }
36696     config = config || {};
36697     
36698     
36699     this.addEvents({
36700         /**
36701          * @event beforenodeedit
36702          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36703          * false from the handler of this event.
36704          * @param {Editor} this
36705          * @param {Roo.tree.Node} node 
36706          */
36707         "beforenodeedit" : true
36708     });
36709     
36710     //Roo.log(config);
36711     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36712
36713     this.tree = tree;
36714
36715     tree.on('beforeclick', this.beforeNodeClick, this);
36716     tree.getTreeEl().on('mousedown', this.hide, this);
36717     this.on('complete', this.updateNode, this);
36718     this.on('beforestartedit', this.fitToTree, this);
36719     this.on('startedit', this.bindScroll, this, {delay:10});
36720     this.on('specialkey', this.onSpecialKey, this);
36721 };
36722
36723 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36724     /**
36725      * @cfg {String} alignment
36726      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36727      */
36728     alignment: "l-l",
36729     // inherit
36730     autoSize: false,
36731     /**
36732      * @cfg {Boolean} hideEl
36733      * True to hide the bound element while the editor is displayed (defaults to false)
36734      */
36735     hideEl : false,
36736     /**
36737      * @cfg {String} cls
36738      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36739      */
36740     cls: "x-small-editor x-tree-editor",
36741     /**
36742      * @cfg {Boolean} shim
36743      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36744      */
36745     shim:false,
36746     // inherit
36747     shadow:"frame",
36748     /**
36749      * @cfg {Number} maxWidth
36750      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36751      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36752      * scroll and client offsets into account prior to each edit.
36753      */
36754     maxWidth: 250,
36755
36756     editDelay : 350,
36757
36758     // private
36759     fitToTree : function(ed, el){
36760         var td = this.tree.getTreeEl().dom, nd = el.dom;
36761         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36762             td.scrollLeft = nd.offsetLeft;
36763         }
36764         var w = Math.min(
36765                 this.maxWidth,
36766                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36767         this.setSize(w, '');
36768         
36769         return this.fireEvent('beforenodeedit', this, this.editNode);
36770         
36771     },
36772
36773     // private
36774     triggerEdit : function(node){
36775         this.completeEdit();
36776         this.editNode = node;
36777         this.startEdit(node.ui.textNode, node.text);
36778     },
36779
36780     // private
36781     bindScroll : function(){
36782         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36783     },
36784
36785     // private
36786     beforeNodeClick : function(node, e){
36787         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36788         this.lastClick = new Date();
36789         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36790             e.stopEvent();
36791             this.triggerEdit(node);
36792             return false;
36793         }
36794         return true;
36795     },
36796
36797     // private
36798     updateNode : function(ed, value){
36799         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36800         this.editNode.setText(value);
36801     },
36802
36803     // private
36804     onHide : function(){
36805         Roo.tree.TreeEditor.superclass.onHide.call(this);
36806         if(this.editNode){
36807             this.editNode.ui.focus();
36808         }
36809     },
36810
36811     // private
36812     onSpecialKey : function(field, e){
36813         var k = e.getKey();
36814         if(k == e.ESC){
36815             e.stopEvent();
36816             this.cancelEdit();
36817         }else if(k == e.ENTER && !e.hasModifier()){
36818             e.stopEvent();
36819             this.completeEdit();
36820         }
36821     }
36822 });//<Script type="text/javascript">
36823 /*
36824  * Based on:
36825  * Ext JS Library 1.1.1
36826  * Copyright(c) 2006-2007, Ext JS, LLC.
36827  *
36828  * Originally Released Under LGPL - original licence link has changed is not relivant.
36829  *
36830  * Fork - LGPL
36831  * <script type="text/javascript">
36832  */
36833  
36834 /**
36835  * Not documented??? - probably should be...
36836  */
36837
36838 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36839     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36840     
36841     renderElements : function(n, a, targetNode, bulkRender){
36842         //consel.log("renderElements?");
36843         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36844
36845         var t = n.getOwnerTree();
36846         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36847         
36848         var cols = t.columns;
36849         var bw = t.borderWidth;
36850         var c = cols[0];
36851         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36852          var cb = typeof a.checked == "boolean";
36853         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36854         var colcls = 'x-t-' + tid + '-c0';
36855         var buf = [
36856             '<li class="x-tree-node">',
36857             
36858                 
36859                 '<div class="x-tree-node-el ', a.cls,'">',
36860                     // extran...
36861                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36862                 
36863                 
36864                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36865                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36866                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36867                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36868                            (a.iconCls ? ' '+a.iconCls : ''),
36869                            '" unselectable="on" />',
36870                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36871                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36872                              
36873                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36874                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36875                             '<span unselectable="on" qtip="' + tx + '">',
36876                              tx,
36877                              '</span></a>' ,
36878                     '</div>',
36879                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36880                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36881                  ];
36882         for(var i = 1, len = cols.length; i < len; i++){
36883             c = cols[i];
36884             colcls = 'x-t-' + tid + '-c' +i;
36885             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36886             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36887                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
36888                       "</div>");
36889          }
36890          
36891          buf.push(
36892             '</a>',
36893             '<div class="x-clear"></div></div>',
36894             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36895             "</li>");
36896         
36897         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36898             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36899                                 n.nextSibling.ui.getEl(), buf.join(""));
36900         }else{
36901             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36902         }
36903         var el = this.wrap.firstChild;
36904         this.elRow = el;
36905         this.elNode = el.firstChild;
36906         this.ranchor = el.childNodes[1];
36907         this.ctNode = this.wrap.childNodes[1];
36908         var cs = el.firstChild.childNodes;
36909         this.indentNode = cs[0];
36910         this.ecNode = cs[1];
36911         this.iconNode = cs[2];
36912         var index = 3;
36913         if(cb){
36914             this.checkbox = cs[3];
36915             index++;
36916         }
36917         this.anchor = cs[index];
36918         
36919         this.textNode = cs[index].firstChild;
36920         
36921         //el.on("click", this.onClick, this);
36922         //el.on("dblclick", this.onDblClick, this);
36923         
36924         
36925        // console.log(this);
36926     },
36927     initEvents : function(){
36928         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
36929         
36930             
36931         var a = this.ranchor;
36932
36933         var el = Roo.get(a);
36934
36935         if(Roo.isOpera){ // opera render bug ignores the CSS
36936             el.setStyle("text-decoration", "none");
36937         }
36938
36939         el.on("click", this.onClick, this);
36940         el.on("dblclick", this.onDblClick, this);
36941         el.on("contextmenu", this.onContextMenu, this);
36942         
36943     },
36944     
36945     /*onSelectedChange : function(state){
36946         if(state){
36947             this.focus();
36948             this.addClass("x-tree-selected");
36949         }else{
36950             //this.blur();
36951             this.removeClass("x-tree-selected");
36952         }
36953     },*/
36954     addClass : function(cls){
36955         if(this.elRow){
36956             Roo.fly(this.elRow).addClass(cls);
36957         }
36958         
36959     },
36960     
36961     
36962     removeClass : function(cls){
36963         if(this.elRow){
36964             Roo.fly(this.elRow).removeClass(cls);
36965         }
36966     }
36967
36968     
36969     
36970 });//<Script type="text/javascript">
36971
36972 /*
36973  * Based on:
36974  * Ext JS Library 1.1.1
36975  * Copyright(c) 2006-2007, Ext JS, LLC.
36976  *
36977  * Originally Released Under LGPL - original licence link has changed is not relivant.
36978  *
36979  * Fork - LGPL
36980  * <script type="text/javascript">
36981  */
36982  
36983
36984 /**
36985  * @class Roo.tree.ColumnTree
36986  * @extends Roo.data.TreePanel
36987  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
36988  * @cfg {int} borderWidth  compined right/left border allowance
36989  * @constructor
36990  * @param {String/HTMLElement/Element} el The container element
36991  * @param {Object} config
36992  */
36993 Roo.tree.ColumnTree =  function(el, config)
36994 {
36995    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
36996    this.addEvents({
36997         /**
36998         * @event resize
36999         * Fire this event on a container when it resizes
37000         * @param {int} w Width
37001         * @param {int} h Height
37002         */
37003        "resize" : true
37004     });
37005     this.on('resize', this.onResize, this);
37006 };
37007
37008 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37009     //lines:false,
37010     
37011     
37012     borderWidth: Roo.isBorderBox ? 0 : 2, 
37013     headEls : false,
37014     
37015     render : function(){
37016         // add the header.....
37017        
37018         Roo.tree.ColumnTree.superclass.render.apply(this);
37019         
37020         this.el.addClass('x-column-tree');
37021         
37022         this.headers = this.el.createChild(
37023             {cls:'x-tree-headers'},this.innerCt.dom);
37024    
37025         var cols = this.columns, c;
37026         var totalWidth = 0;
37027         this.headEls = [];
37028         var  len = cols.length;
37029         for(var i = 0; i < len; i++){
37030              c = cols[i];
37031              totalWidth += c.width;
37032             this.headEls.push(this.headers.createChild({
37033                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37034                  cn: {
37035                      cls:'x-tree-hd-text',
37036                      html: c.header
37037                  },
37038                  style:'width:'+(c.width-this.borderWidth)+'px;'
37039              }));
37040         }
37041         this.headers.createChild({cls:'x-clear'});
37042         // prevent floats from wrapping when clipped
37043         this.headers.setWidth(totalWidth);
37044         //this.innerCt.setWidth(totalWidth);
37045         this.innerCt.setStyle({ overflow: 'auto' });
37046         this.onResize(this.width, this.height);
37047              
37048         
37049     },
37050     onResize : function(w,h)
37051     {
37052         this.height = h;
37053         this.width = w;
37054         // resize cols..
37055         this.innerCt.setWidth(this.width);
37056         this.innerCt.setHeight(this.height-20);
37057         
37058         // headers...
37059         var cols = this.columns, c;
37060         var totalWidth = 0;
37061         var expEl = false;
37062         var len = cols.length;
37063         for(var i = 0; i < len; i++){
37064             c = cols[i];
37065             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37066                 // it's the expander..
37067                 expEl  = this.headEls[i];
37068                 continue;
37069             }
37070             totalWidth += c.width;
37071             
37072         }
37073         if (expEl) {
37074             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37075         }
37076         this.headers.setWidth(w-20);
37077
37078         
37079         
37080         
37081     }
37082 });
37083 /*
37084  * Based on:
37085  * Ext JS Library 1.1.1
37086  * Copyright(c) 2006-2007, Ext JS, LLC.
37087  *
37088  * Originally Released Under LGPL - original licence link has changed is not relivant.
37089  *
37090  * Fork - LGPL
37091  * <script type="text/javascript">
37092  */
37093  
37094 /**
37095  * @class Roo.menu.Menu
37096  * @extends Roo.util.Observable
37097  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37098  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37099  * @constructor
37100  * Creates a new Menu
37101  * @param {Object} config Configuration options
37102  */
37103 Roo.menu.Menu = function(config){
37104     Roo.apply(this, config);
37105     this.id = this.id || Roo.id();
37106     this.addEvents({
37107         /**
37108          * @event beforeshow
37109          * Fires before this menu is displayed
37110          * @param {Roo.menu.Menu} this
37111          */
37112         beforeshow : true,
37113         /**
37114          * @event beforehide
37115          * Fires before this menu is hidden
37116          * @param {Roo.menu.Menu} this
37117          */
37118         beforehide : true,
37119         /**
37120          * @event show
37121          * Fires after this menu is displayed
37122          * @param {Roo.menu.Menu} this
37123          */
37124         show : true,
37125         /**
37126          * @event hide
37127          * Fires after this menu is hidden
37128          * @param {Roo.menu.Menu} this
37129          */
37130         hide : true,
37131         /**
37132          * @event click
37133          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37134          * @param {Roo.menu.Menu} this
37135          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37136          * @param {Roo.EventObject} e
37137          */
37138         click : true,
37139         /**
37140          * @event mouseover
37141          * Fires when the mouse is hovering over this menu
37142          * @param {Roo.menu.Menu} this
37143          * @param {Roo.EventObject} e
37144          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37145          */
37146         mouseover : true,
37147         /**
37148          * @event mouseout
37149          * Fires when the mouse exits this menu
37150          * @param {Roo.menu.Menu} this
37151          * @param {Roo.EventObject} e
37152          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37153          */
37154         mouseout : true,
37155         /**
37156          * @event itemclick
37157          * Fires when a menu item contained in this menu is clicked
37158          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37159          * @param {Roo.EventObject} e
37160          */
37161         itemclick: true
37162     });
37163     if (this.registerMenu) {
37164         Roo.menu.MenuMgr.register(this);
37165     }
37166     
37167     var mis = this.items;
37168     this.items = new Roo.util.MixedCollection();
37169     if(mis){
37170         this.add.apply(this, mis);
37171     }
37172 };
37173
37174 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37175     /**
37176      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37177      */
37178     minWidth : 120,
37179     /**
37180      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37181      * for bottom-right shadow (defaults to "sides")
37182      */
37183     shadow : "sides",
37184     /**
37185      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37186      * this menu (defaults to "tl-tr?")
37187      */
37188     subMenuAlign : "tl-tr?",
37189     /**
37190      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37191      * relative to its element of origin (defaults to "tl-bl?")
37192      */
37193     defaultAlign : "tl-bl?",
37194     /**
37195      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37196      */
37197     allowOtherMenus : false,
37198     /**
37199      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37200      */
37201     registerMenu : true,
37202
37203     hidden:true,
37204
37205     // private
37206     render : function(){
37207         if(this.el){
37208             return;
37209         }
37210         var el = this.el = new Roo.Layer({
37211             cls: "x-menu",
37212             shadow:this.shadow,
37213             constrain: false,
37214             parentEl: this.parentEl || document.body,
37215             zindex:15000
37216         });
37217
37218         this.keyNav = new Roo.menu.MenuNav(this);
37219
37220         if(this.plain){
37221             el.addClass("x-menu-plain");
37222         }
37223         if(this.cls){
37224             el.addClass(this.cls);
37225         }
37226         // generic focus element
37227         this.focusEl = el.createChild({
37228             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37229         });
37230         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37231         //disabling touch- as it's causing issues ..
37232         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37233         ul.on('click'   , this.onClick, this);
37234         
37235         
37236         ul.on("mouseover", this.onMouseOver, this);
37237         ul.on("mouseout", this.onMouseOut, this);
37238         this.items.each(function(item){
37239             if (item.hidden) {
37240                 return;
37241             }
37242             
37243             var li = document.createElement("li");
37244             li.className = "x-menu-list-item";
37245             ul.dom.appendChild(li);
37246             item.render(li, this);
37247         }, this);
37248         this.ul = ul;
37249         this.autoWidth();
37250     },
37251
37252     // private
37253     autoWidth : function(){
37254         var el = this.el, ul = this.ul;
37255         if(!el){
37256             return;
37257         }
37258         var w = this.width;
37259         if(w){
37260             el.setWidth(w);
37261         }else if(Roo.isIE){
37262             el.setWidth(this.minWidth);
37263             var t = el.dom.offsetWidth; // force recalc
37264             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37265         }
37266     },
37267
37268     // private
37269     delayAutoWidth : function(){
37270         if(this.rendered){
37271             if(!this.awTask){
37272                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37273             }
37274             this.awTask.delay(20);
37275         }
37276     },
37277
37278     // private
37279     findTargetItem : function(e){
37280         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37281         if(t && t.menuItemId){
37282             return this.items.get(t.menuItemId);
37283         }
37284     },
37285
37286     // private
37287     onClick : function(e){
37288         Roo.log("menu.onClick");
37289         var t = this.findTargetItem(e);
37290         if(!t){
37291             return;
37292         }
37293         Roo.log(e);
37294         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37295             if(t == this.activeItem && t.shouldDeactivate(e)){
37296                 this.activeItem.deactivate();
37297                 delete this.activeItem;
37298                 return;
37299             }
37300             if(t.canActivate){
37301                 this.setActiveItem(t, true);
37302             }
37303             return;
37304             
37305             
37306         }
37307         
37308         t.onClick(e);
37309         this.fireEvent("click", this, t, e);
37310     },
37311
37312     // private
37313     setActiveItem : function(item, autoExpand){
37314         if(item != this.activeItem){
37315             if(this.activeItem){
37316                 this.activeItem.deactivate();
37317             }
37318             this.activeItem = item;
37319             item.activate(autoExpand);
37320         }else if(autoExpand){
37321             item.expandMenu();
37322         }
37323     },
37324
37325     // private
37326     tryActivate : function(start, step){
37327         var items = this.items;
37328         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37329             var item = items.get(i);
37330             if(!item.disabled && item.canActivate){
37331                 this.setActiveItem(item, false);
37332                 return item;
37333             }
37334         }
37335         return false;
37336     },
37337
37338     // private
37339     onMouseOver : function(e){
37340         var t;
37341         if(t = this.findTargetItem(e)){
37342             if(t.canActivate && !t.disabled){
37343                 this.setActiveItem(t, true);
37344             }
37345         }
37346         this.fireEvent("mouseover", this, e, t);
37347     },
37348
37349     // private
37350     onMouseOut : function(e){
37351         var t;
37352         if(t = this.findTargetItem(e)){
37353             if(t == this.activeItem && t.shouldDeactivate(e)){
37354                 this.activeItem.deactivate();
37355                 delete this.activeItem;
37356             }
37357         }
37358         this.fireEvent("mouseout", this, e, t);
37359     },
37360
37361     /**
37362      * Read-only.  Returns true if the menu is currently displayed, else false.
37363      * @type Boolean
37364      */
37365     isVisible : function(){
37366         return this.el && !this.hidden;
37367     },
37368
37369     /**
37370      * Displays this menu relative to another element
37371      * @param {String/HTMLElement/Roo.Element} element The element to align to
37372      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37373      * the element (defaults to this.defaultAlign)
37374      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37375      */
37376     show : function(el, pos, parentMenu){
37377         this.parentMenu = parentMenu;
37378         if(!this.el){
37379             this.render();
37380         }
37381         this.fireEvent("beforeshow", this);
37382         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37383     },
37384
37385     /**
37386      * Displays this menu at a specific xy position
37387      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37388      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37389      */
37390     showAt : function(xy, parentMenu, /* private: */_e){
37391         this.parentMenu = parentMenu;
37392         if(!this.el){
37393             this.render();
37394         }
37395         if(_e !== false){
37396             this.fireEvent("beforeshow", this);
37397             xy = this.el.adjustForConstraints(xy);
37398         }
37399         this.el.setXY(xy);
37400         this.el.show();
37401         this.hidden = false;
37402         this.focus();
37403         this.fireEvent("show", this);
37404     },
37405
37406     focus : function(){
37407         if(!this.hidden){
37408             this.doFocus.defer(50, this);
37409         }
37410     },
37411
37412     doFocus : function(){
37413         if(!this.hidden){
37414             this.focusEl.focus();
37415         }
37416     },
37417
37418     /**
37419      * Hides this menu and optionally all parent menus
37420      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37421      */
37422     hide : function(deep){
37423         if(this.el && this.isVisible()){
37424             this.fireEvent("beforehide", this);
37425             if(this.activeItem){
37426                 this.activeItem.deactivate();
37427                 this.activeItem = null;
37428             }
37429             this.el.hide();
37430             this.hidden = true;
37431             this.fireEvent("hide", this);
37432         }
37433         if(deep === true && this.parentMenu){
37434             this.parentMenu.hide(true);
37435         }
37436     },
37437
37438     /**
37439      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37440      * Any of the following are valid:
37441      * <ul>
37442      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37443      * <li>An HTMLElement object which will be converted to a menu item</li>
37444      * <li>A menu item config object that will be created as a new menu item</li>
37445      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37446      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37447      * </ul>
37448      * Usage:
37449      * <pre><code>
37450 // Create the menu
37451 var menu = new Roo.menu.Menu();
37452
37453 // Create a menu item to add by reference
37454 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37455
37456 // Add a bunch of items at once using different methods.
37457 // Only the last item added will be returned.
37458 var item = menu.add(
37459     menuItem,                // add existing item by ref
37460     'Dynamic Item',          // new TextItem
37461     '-',                     // new separator
37462     { text: 'Config Item' }  // new item by config
37463 );
37464 </code></pre>
37465      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37466      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37467      */
37468     add : function(){
37469         var a = arguments, l = a.length, item;
37470         for(var i = 0; i < l; i++){
37471             var el = a[i];
37472             if ((typeof(el) == "object") && el.xtype && el.xns) {
37473                 el = Roo.factory(el, Roo.menu);
37474             }
37475             
37476             if(el.render){ // some kind of Item
37477                 item = this.addItem(el);
37478             }else if(typeof el == "string"){ // string
37479                 if(el == "separator" || el == "-"){
37480                     item = this.addSeparator();
37481                 }else{
37482                     item = this.addText(el);
37483                 }
37484             }else if(el.tagName || el.el){ // element
37485                 item = this.addElement(el);
37486             }else if(typeof el == "object"){ // must be menu item config?
37487                 item = this.addMenuItem(el);
37488             }
37489         }
37490         return item;
37491     },
37492
37493     /**
37494      * Returns this menu's underlying {@link Roo.Element} object
37495      * @return {Roo.Element} The element
37496      */
37497     getEl : function(){
37498         if(!this.el){
37499             this.render();
37500         }
37501         return this.el;
37502     },
37503
37504     /**
37505      * Adds a separator bar to the menu
37506      * @return {Roo.menu.Item} The menu item that was added
37507      */
37508     addSeparator : function(){
37509         return this.addItem(new Roo.menu.Separator());
37510     },
37511
37512     /**
37513      * Adds an {@link Roo.Element} object to the menu
37514      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37515      * @return {Roo.menu.Item} The menu item that was added
37516      */
37517     addElement : function(el){
37518         return this.addItem(new Roo.menu.BaseItem(el));
37519     },
37520
37521     /**
37522      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37523      * @param {Roo.menu.Item} item The menu item to add
37524      * @return {Roo.menu.Item} The menu item that was added
37525      */
37526     addItem : function(item){
37527         this.items.add(item);
37528         if(this.ul){
37529             var li = document.createElement("li");
37530             li.className = "x-menu-list-item";
37531             this.ul.dom.appendChild(li);
37532             item.render(li, this);
37533             this.delayAutoWidth();
37534         }
37535         return item;
37536     },
37537
37538     /**
37539      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37540      * @param {Object} config A MenuItem config object
37541      * @return {Roo.menu.Item} The menu item that was added
37542      */
37543     addMenuItem : function(config){
37544         if(!(config instanceof Roo.menu.Item)){
37545             if(typeof config.checked == "boolean"){ // must be check menu item config?
37546                 config = new Roo.menu.CheckItem(config);
37547             }else{
37548                 config = new Roo.menu.Item(config);
37549             }
37550         }
37551         return this.addItem(config);
37552     },
37553
37554     /**
37555      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37556      * @param {String} text The text to display in the menu item
37557      * @return {Roo.menu.Item} The menu item that was added
37558      */
37559     addText : function(text){
37560         return this.addItem(new Roo.menu.TextItem({ text : text }));
37561     },
37562
37563     /**
37564      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37565      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37566      * @param {Roo.menu.Item} item The menu item to add
37567      * @return {Roo.menu.Item} The menu item that was added
37568      */
37569     insert : function(index, item){
37570         this.items.insert(index, item);
37571         if(this.ul){
37572             var li = document.createElement("li");
37573             li.className = "x-menu-list-item";
37574             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37575             item.render(li, this);
37576             this.delayAutoWidth();
37577         }
37578         return item;
37579     },
37580
37581     /**
37582      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37583      * @param {Roo.menu.Item} item The menu item to remove
37584      */
37585     remove : function(item){
37586         this.items.removeKey(item.id);
37587         item.destroy();
37588     },
37589
37590     /**
37591      * Removes and destroys all items in the menu
37592      */
37593     removeAll : function(){
37594         var f;
37595         while(f = this.items.first()){
37596             this.remove(f);
37597         }
37598     }
37599 });
37600
37601 // MenuNav is a private utility class used internally by the Menu
37602 Roo.menu.MenuNav = function(menu){
37603     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37604     this.scope = this.menu = menu;
37605 };
37606
37607 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37608     doRelay : function(e, h){
37609         var k = e.getKey();
37610         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37611             this.menu.tryActivate(0, 1);
37612             return false;
37613         }
37614         return h.call(this.scope || this, e, this.menu);
37615     },
37616
37617     up : function(e, m){
37618         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37619             m.tryActivate(m.items.length-1, -1);
37620         }
37621     },
37622
37623     down : function(e, m){
37624         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37625             m.tryActivate(0, 1);
37626         }
37627     },
37628
37629     right : function(e, m){
37630         if(m.activeItem){
37631             m.activeItem.expandMenu(true);
37632         }
37633     },
37634
37635     left : function(e, m){
37636         m.hide();
37637         if(m.parentMenu && m.parentMenu.activeItem){
37638             m.parentMenu.activeItem.activate();
37639         }
37640     },
37641
37642     enter : function(e, m){
37643         if(m.activeItem){
37644             e.stopPropagation();
37645             m.activeItem.onClick(e);
37646             m.fireEvent("click", this, m.activeItem);
37647             return true;
37648         }
37649     }
37650 });/*
37651  * Based on:
37652  * Ext JS Library 1.1.1
37653  * Copyright(c) 2006-2007, Ext JS, LLC.
37654  *
37655  * Originally Released Under LGPL - original licence link has changed is not relivant.
37656  *
37657  * Fork - LGPL
37658  * <script type="text/javascript">
37659  */
37660  
37661 /**
37662  * @class Roo.menu.MenuMgr
37663  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37664  * @singleton
37665  */
37666 Roo.menu.MenuMgr = function(){
37667    var menus, active, groups = {}, attached = false, lastShow = new Date();
37668
37669    // private - called when first menu is created
37670    function init(){
37671        menus = {};
37672        active = new Roo.util.MixedCollection();
37673        Roo.get(document).addKeyListener(27, function(){
37674            if(active.length > 0){
37675                hideAll();
37676            }
37677        });
37678    }
37679
37680    // private
37681    function hideAll(){
37682        if(active && active.length > 0){
37683            var c = active.clone();
37684            c.each(function(m){
37685                m.hide();
37686            });
37687        }
37688    }
37689
37690    // private
37691    function onHide(m){
37692        active.remove(m);
37693        if(active.length < 1){
37694            Roo.get(document).un("mousedown", onMouseDown);
37695            attached = false;
37696        }
37697    }
37698
37699    // private
37700    function onShow(m){
37701        var last = active.last();
37702        lastShow = new Date();
37703        active.add(m);
37704        if(!attached){
37705            Roo.get(document).on("mousedown", onMouseDown);
37706            attached = true;
37707        }
37708        if(m.parentMenu){
37709           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37710           m.parentMenu.activeChild = m;
37711        }else if(last && last.isVisible()){
37712           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37713        }
37714    }
37715
37716    // private
37717    function onBeforeHide(m){
37718        if(m.activeChild){
37719            m.activeChild.hide();
37720        }
37721        if(m.autoHideTimer){
37722            clearTimeout(m.autoHideTimer);
37723            delete m.autoHideTimer;
37724        }
37725    }
37726
37727    // private
37728    function onBeforeShow(m){
37729        var pm = m.parentMenu;
37730        if(!pm && !m.allowOtherMenus){
37731            hideAll();
37732        }else if(pm && pm.activeChild && active != m){
37733            pm.activeChild.hide();
37734        }
37735    }
37736
37737    // private
37738    function onMouseDown(e){
37739        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37740            hideAll();
37741        }
37742    }
37743
37744    // private
37745    function onBeforeCheck(mi, state){
37746        if(state){
37747            var g = groups[mi.group];
37748            for(var i = 0, l = g.length; i < l; i++){
37749                if(g[i] != mi){
37750                    g[i].setChecked(false);
37751                }
37752            }
37753        }
37754    }
37755
37756    return {
37757
37758        /**
37759         * Hides all menus that are currently visible
37760         */
37761        hideAll : function(){
37762             hideAll();  
37763        },
37764
37765        // private
37766        register : function(menu){
37767            if(!menus){
37768                init();
37769            }
37770            menus[menu.id] = menu;
37771            menu.on("beforehide", onBeforeHide);
37772            menu.on("hide", onHide);
37773            menu.on("beforeshow", onBeforeShow);
37774            menu.on("show", onShow);
37775            var g = menu.group;
37776            if(g && menu.events["checkchange"]){
37777                if(!groups[g]){
37778                    groups[g] = [];
37779                }
37780                groups[g].push(menu);
37781                menu.on("checkchange", onCheck);
37782            }
37783        },
37784
37785         /**
37786          * Returns a {@link Roo.menu.Menu} object
37787          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37788          * be used to generate and return a new Menu instance.
37789          */
37790        get : function(menu){
37791            if(typeof menu == "string"){ // menu id
37792                return menus[menu];
37793            }else if(menu.events){  // menu instance
37794                return menu;
37795            }else if(typeof menu.length == 'number'){ // array of menu items?
37796                return new Roo.menu.Menu({items:menu});
37797            }else{ // otherwise, must be a config
37798                return new Roo.menu.Menu(menu);
37799            }
37800        },
37801
37802        // private
37803        unregister : function(menu){
37804            delete menus[menu.id];
37805            menu.un("beforehide", onBeforeHide);
37806            menu.un("hide", onHide);
37807            menu.un("beforeshow", onBeforeShow);
37808            menu.un("show", onShow);
37809            var g = menu.group;
37810            if(g && menu.events["checkchange"]){
37811                groups[g].remove(menu);
37812                menu.un("checkchange", onCheck);
37813            }
37814        },
37815
37816        // private
37817        registerCheckable : function(menuItem){
37818            var g = menuItem.group;
37819            if(g){
37820                if(!groups[g]){
37821                    groups[g] = [];
37822                }
37823                groups[g].push(menuItem);
37824                menuItem.on("beforecheckchange", onBeforeCheck);
37825            }
37826        },
37827
37828        // private
37829        unregisterCheckable : function(menuItem){
37830            var g = menuItem.group;
37831            if(g){
37832                groups[g].remove(menuItem);
37833                menuItem.un("beforecheckchange", onBeforeCheck);
37834            }
37835        }
37836    };
37837 }();/*
37838  * Based on:
37839  * Ext JS Library 1.1.1
37840  * Copyright(c) 2006-2007, Ext JS, LLC.
37841  *
37842  * Originally Released Under LGPL - original licence link has changed is not relivant.
37843  *
37844  * Fork - LGPL
37845  * <script type="text/javascript">
37846  */
37847  
37848
37849 /**
37850  * @class Roo.menu.BaseItem
37851  * @extends Roo.Component
37852  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37853  * management and base configuration options shared by all menu components.
37854  * @constructor
37855  * Creates a new BaseItem
37856  * @param {Object} config Configuration options
37857  */
37858 Roo.menu.BaseItem = function(config){
37859     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37860
37861     this.addEvents({
37862         /**
37863          * @event click
37864          * Fires when this item is clicked
37865          * @param {Roo.menu.BaseItem} this
37866          * @param {Roo.EventObject} e
37867          */
37868         click: true,
37869         /**
37870          * @event activate
37871          * Fires when this item is activated
37872          * @param {Roo.menu.BaseItem} this
37873          */
37874         activate : true,
37875         /**
37876          * @event deactivate
37877          * Fires when this item is deactivated
37878          * @param {Roo.menu.BaseItem} this
37879          */
37880         deactivate : true
37881     });
37882
37883     if(this.handler){
37884         this.on("click", this.handler, this.scope, true);
37885     }
37886 };
37887
37888 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
37889     /**
37890      * @cfg {Function} handler
37891      * A function that will handle the click event of this menu item (defaults to undefined)
37892      */
37893     /**
37894      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
37895      */
37896     canActivate : false,
37897     
37898      /**
37899      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
37900      */
37901     hidden: false,
37902     
37903     /**
37904      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
37905      */
37906     activeClass : "x-menu-item-active",
37907     /**
37908      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
37909      */
37910     hideOnClick : true,
37911     /**
37912      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
37913      */
37914     hideDelay : 100,
37915
37916     // private
37917     ctype: "Roo.menu.BaseItem",
37918
37919     // private
37920     actionMode : "container",
37921
37922     // private
37923     render : function(container, parentMenu){
37924         this.parentMenu = parentMenu;
37925         Roo.menu.BaseItem.superclass.render.call(this, container);
37926         this.container.menuItemId = this.id;
37927     },
37928
37929     // private
37930     onRender : function(container, position){
37931         this.el = Roo.get(this.el);
37932         container.dom.appendChild(this.el.dom);
37933     },
37934
37935     // private
37936     onClick : function(e){
37937         if(!this.disabled && this.fireEvent("click", this, e) !== false
37938                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
37939             this.handleClick(e);
37940         }else{
37941             e.stopEvent();
37942         }
37943     },
37944
37945     // private
37946     activate : function(){
37947         if(this.disabled){
37948             return false;
37949         }
37950         var li = this.container;
37951         li.addClass(this.activeClass);
37952         this.region = li.getRegion().adjust(2, 2, -2, -2);
37953         this.fireEvent("activate", this);
37954         return true;
37955     },
37956
37957     // private
37958     deactivate : function(){
37959         this.container.removeClass(this.activeClass);
37960         this.fireEvent("deactivate", this);
37961     },
37962
37963     // private
37964     shouldDeactivate : function(e){
37965         return !this.region || !this.region.contains(e.getPoint());
37966     },
37967
37968     // private
37969     handleClick : function(e){
37970         if(this.hideOnClick){
37971             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
37972         }
37973     },
37974
37975     // private
37976     expandMenu : function(autoActivate){
37977         // do nothing
37978     },
37979
37980     // private
37981     hideMenu : function(){
37982         // do nothing
37983     }
37984 });/*
37985  * Based on:
37986  * Ext JS Library 1.1.1
37987  * Copyright(c) 2006-2007, Ext JS, LLC.
37988  *
37989  * Originally Released Under LGPL - original licence link has changed is not relivant.
37990  *
37991  * Fork - LGPL
37992  * <script type="text/javascript">
37993  */
37994  
37995 /**
37996  * @class Roo.menu.Adapter
37997  * @extends Roo.menu.BaseItem
37998  * 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.
37999  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38000  * @constructor
38001  * Creates a new Adapter
38002  * @param {Object} config Configuration options
38003  */
38004 Roo.menu.Adapter = function(component, config){
38005     Roo.menu.Adapter.superclass.constructor.call(this, config);
38006     this.component = component;
38007 };
38008 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38009     // private
38010     canActivate : true,
38011
38012     // private
38013     onRender : function(container, position){
38014         this.component.render(container);
38015         this.el = this.component.getEl();
38016     },
38017
38018     // private
38019     activate : function(){
38020         if(this.disabled){
38021             return false;
38022         }
38023         this.component.focus();
38024         this.fireEvent("activate", this);
38025         return true;
38026     },
38027
38028     // private
38029     deactivate : function(){
38030         this.fireEvent("deactivate", this);
38031     },
38032
38033     // private
38034     disable : function(){
38035         this.component.disable();
38036         Roo.menu.Adapter.superclass.disable.call(this);
38037     },
38038
38039     // private
38040     enable : function(){
38041         this.component.enable();
38042         Roo.menu.Adapter.superclass.enable.call(this);
38043     }
38044 });/*
38045  * Based on:
38046  * Ext JS Library 1.1.1
38047  * Copyright(c) 2006-2007, Ext JS, LLC.
38048  *
38049  * Originally Released Under LGPL - original licence link has changed is not relivant.
38050  *
38051  * Fork - LGPL
38052  * <script type="text/javascript">
38053  */
38054
38055 /**
38056  * @class Roo.menu.TextItem
38057  * @extends Roo.menu.BaseItem
38058  * Adds a static text string to a menu, usually used as either a heading or group separator.
38059  * Note: old style constructor with text is still supported.
38060  * 
38061  * @constructor
38062  * Creates a new TextItem
38063  * @param {Object} cfg Configuration
38064  */
38065 Roo.menu.TextItem = function(cfg){
38066     if (typeof(cfg) == 'string') {
38067         this.text = cfg;
38068     } else {
38069         Roo.apply(this,cfg);
38070     }
38071     
38072     Roo.menu.TextItem.superclass.constructor.call(this);
38073 };
38074
38075 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38076     /**
38077      * @cfg {Boolean} text Text to show on item.
38078      */
38079     text : '',
38080     
38081     /**
38082      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38083      */
38084     hideOnClick : false,
38085     /**
38086      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38087      */
38088     itemCls : "x-menu-text",
38089
38090     // private
38091     onRender : function(){
38092         var s = document.createElement("span");
38093         s.className = this.itemCls;
38094         s.innerHTML = this.text;
38095         this.el = s;
38096         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38097     }
38098 });/*
38099  * Based on:
38100  * Ext JS Library 1.1.1
38101  * Copyright(c) 2006-2007, Ext JS, LLC.
38102  *
38103  * Originally Released Under LGPL - original licence link has changed is not relivant.
38104  *
38105  * Fork - LGPL
38106  * <script type="text/javascript">
38107  */
38108
38109 /**
38110  * @class Roo.menu.Separator
38111  * @extends Roo.menu.BaseItem
38112  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38113  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38114  * @constructor
38115  * @param {Object} config Configuration options
38116  */
38117 Roo.menu.Separator = function(config){
38118     Roo.menu.Separator.superclass.constructor.call(this, config);
38119 };
38120
38121 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38122     /**
38123      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38124      */
38125     itemCls : "x-menu-sep",
38126     /**
38127      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38128      */
38129     hideOnClick : false,
38130
38131     // private
38132     onRender : function(li){
38133         var s = document.createElement("span");
38134         s.className = this.itemCls;
38135         s.innerHTML = "&#160;";
38136         this.el = s;
38137         li.addClass("x-menu-sep-li");
38138         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38139     }
38140 });/*
38141  * Based on:
38142  * Ext JS Library 1.1.1
38143  * Copyright(c) 2006-2007, Ext JS, LLC.
38144  *
38145  * Originally Released Under LGPL - original licence link has changed is not relivant.
38146  *
38147  * Fork - LGPL
38148  * <script type="text/javascript">
38149  */
38150 /**
38151  * @class Roo.menu.Item
38152  * @extends Roo.menu.BaseItem
38153  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38154  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38155  * activation and click handling.
38156  * @constructor
38157  * Creates a new Item
38158  * @param {Object} config Configuration options
38159  */
38160 Roo.menu.Item = function(config){
38161     Roo.menu.Item.superclass.constructor.call(this, config);
38162     if(this.menu){
38163         this.menu = Roo.menu.MenuMgr.get(this.menu);
38164     }
38165 };
38166 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38167     
38168     /**
38169      * @cfg {String} text
38170      * The text to show on the menu item.
38171      */
38172     text: '',
38173      /**
38174      * @cfg {String} HTML to render in menu
38175      * The text to show on the menu item (HTML version).
38176      */
38177     html: '',
38178     /**
38179      * @cfg {String} icon
38180      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38181      */
38182     icon: undefined,
38183     /**
38184      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38185      */
38186     itemCls : "x-menu-item",
38187     /**
38188      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38189      */
38190     canActivate : true,
38191     /**
38192      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38193      */
38194     showDelay: 200,
38195     // doc'd in BaseItem
38196     hideDelay: 200,
38197
38198     // private
38199     ctype: "Roo.menu.Item",
38200     
38201     // private
38202     onRender : function(container, position){
38203         var el = document.createElement("a");
38204         el.hideFocus = true;
38205         el.unselectable = "on";
38206         el.href = this.href || "#";
38207         if(this.hrefTarget){
38208             el.target = this.hrefTarget;
38209         }
38210         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38211         
38212         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38213         
38214         el.innerHTML = String.format(
38215                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38216                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38217         this.el = el;
38218         Roo.menu.Item.superclass.onRender.call(this, container, position);
38219     },
38220
38221     /**
38222      * Sets the text to display in this menu item
38223      * @param {String} text The text to display
38224      * @param {Boolean} isHTML true to indicate text is pure html.
38225      */
38226     setText : function(text, isHTML){
38227         if (isHTML) {
38228             this.html = text;
38229         } else {
38230             this.text = text;
38231             this.html = '';
38232         }
38233         if(this.rendered){
38234             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38235      
38236             this.el.update(String.format(
38237                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38238                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38239             this.parentMenu.autoWidth();
38240         }
38241     },
38242
38243     // private
38244     handleClick : function(e){
38245         if(!this.href){ // if no link defined, stop the event automatically
38246             e.stopEvent();
38247         }
38248         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38249     },
38250
38251     // private
38252     activate : function(autoExpand){
38253         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38254             this.focus();
38255             if(autoExpand){
38256                 this.expandMenu();
38257             }
38258         }
38259         return true;
38260     },
38261
38262     // private
38263     shouldDeactivate : function(e){
38264         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38265             if(this.menu && this.menu.isVisible()){
38266                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38267             }
38268             return true;
38269         }
38270         return false;
38271     },
38272
38273     // private
38274     deactivate : function(){
38275         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38276         this.hideMenu();
38277     },
38278
38279     // private
38280     expandMenu : function(autoActivate){
38281         if(!this.disabled && this.menu){
38282             clearTimeout(this.hideTimer);
38283             delete this.hideTimer;
38284             if(!this.menu.isVisible() && !this.showTimer){
38285                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38286             }else if (this.menu.isVisible() && autoActivate){
38287                 this.menu.tryActivate(0, 1);
38288             }
38289         }
38290     },
38291
38292     // private
38293     deferExpand : function(autoActivate){
38294         delete this.showTimer;
38295         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38296         if(autoActivate){
38297             this.menu.tryActivate(0, 1);
38298         }
38299     },
38300
38301     // private
38302     hideMenu : function(){
38303         clearTimeout(this.showTimer);
38304         delete this.showTimer;
38305         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38306             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38307         }
38308     },
38309
38310     // private
38311     deferHide : function(){
38312         delete this.hideTimer;
38313         this.menu.hide();
38314     }
38315 });/*
38316  * Based on:
38317  * Ext JS Library 1.1.1
38318  * Copyright(c) 2006-2007, Ext JS, LLC.
38319  *
38320  * Originally Released Under LGPL - original licence link has changed is not relivant.
38321  *
38322  * Fork - LGPL
38323  * <script type="text/javascript">
38324  */
38325  
38326 /**
38327  * @class Roo.menu.CheckItem
38328  * @extends Roo.menu.Item
38329  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38330  * @constructor
38331  * Creates a new CheckItem
38332  * @param {Object} config Configuration options
38333  */
38334 Roo.menu.CheckItem = function(config){
38335     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38336     this.addEvents({
38337         /**
38338          * @event beforecheckchange
38339          * Fires before the checked value is set, providing an opportunity to cancel if needed
38340          * @param {Roo.menu.CheckItem} this
38341          * @param {Boolean} checked The new checked value that will be set
38342          */
38343         "beforecheckchange" : true,
38344         /**
38345          * @event checkchange
38346          * Fires after the checked value has been set
38347          * @param {Roo.menu.CheckItem} this
38348          * @param {Boolean} checked The checked value that was set
38349          */
38350         "checkchange" : true
38351     });
38352     if(this.checkHandler){
38353         this.on('checkchange', this.checkHandler, this.scope);
38354     }
38355 };
38356 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38357     /**
38358      * @cfg {String} group
38359      * All check items with the same group name will automatically be grouped into a single-select
38360      * radio button group (defaults to '')
38361      */
38362     /**
38363      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38364      */
38365     itemCls : "x-menu-item x-menu-check-item",
38366     /**
38367      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38368      */
38369     groupClass : "x-menu-group-item",
38370
38371     /**
38372      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38373      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38374      * initialized with checked = true will be rendered as checked.
38375      */
38376     checked: false,
38377
38378     // private
38379     ctype: "Roo.menu.CheckItem",
38380
38381     // private
38382     onRender : function(c){
38383         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38384         if(this.group){
38385             this.el.addClass(this.groupClass);
38386         }
38387         Roo.menu.MenuMgr.registerCheckable(this);
38388         if(this.checked){
38389             this.checked = false;
38390             this.setChecked(true, true);
38391         }
38392     },
38393
38394     // private
38395     destroy : function(){
38396         if(this.rendered){
38397             Roo.menu.MenuMgr.unregisterCheckable(this);
38398         }
38399         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38400     },
38401
38402     /**
38403      * Set the checked state of this item
38404      * @param {Boolean} checked The new checked value
38405      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38406      */
38407     setChecked : function(state, suppressEvent){
38408         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38409             if(this.container){
38410                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38411             }
38412             this.checked = state;
38413             if(suppressEvent !== true){
38414                 this.fireEvent("checkchange", this, state);
38415             }
38416         }
38417     },
38418
38419     // private
38420     handleClick : function(e){
38421        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38422            this.setChecked(!this.checked);
38423        }
38424        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38425     }
38426 });/*
38427  * Based on:
38428  * Ext JS Library 1.1.1
38429  * Copyright(c) 2006-2007, Ext JS, LLC.
38430  *
38431  * Originally Released Under LGPL - original licence link has changed is not relivant.
38432  *
38433  * Fork - LGPL
38434  * <script type="text/javascript">
38435  */
38436  
38437 /**
38438  * @class Roo.menu.DateItem
38439  * @extends Roo.menu.Adapter
38440  * A menu item that wraps the {@link Roo.DatPicker} component.
38441  * @constructor
38442  * Creates a new DateItem
38443  * @param {Object} config Configuration options
38444  */
38445 Roo.menu.DateItem = function(config){
38446     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38447     /** The Roo.DatePicker object @type Roo.DatePicker */
38448     this.picker = this.component;
38449     this.addEvents({select: true});
38450     
38451     this.picker.on("render", function(picker){
38452         picker.getEl().swallowEvent("click");
38453         picker.container.addClass("x-menu-date-item");
38454     });
38455
38456     this.picker.on("select", this.onSelect, this);
38457 };
38458
38459 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38460     // private
38461     onSelect : function(picker, date){
38462         this.fireEvent("select", this, date, picker);
38463         Roo.menu.DateItem.superclass.handleClick.call(this);
38464     }
38465 });/*
38466  * Based on:
38467  * Ext JS Library 1.1.1
38468  * Copyright(c) 2006-2007, Ext JS, LLC.
38469  *
38470  * Originally Released Under LGPL - original licence link has changed is not relivant.
38471  *
38472  * Fork - LGPL
38473  * <script type="text/javascript">
38474  */
38475  
38476 /**
38477  * @class Roo.menu.ColorItem
38478  * @extends Roo.menu.Adapter
38479  * A menu item that wraps the {@link Roo.ColorPalette} component.
38480  * @constructor
38481  * Creates a new ColorItem
38482  * @param {Object} config Configuration options
38483  */
38484 Roo.menu.ColorItem = function(config){
38485     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38486     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38487     this.palette = this.component;
38488     this.relayEvents(this.palette, ["select"]);
38489     if(this.selectHandler){
38490         this.on('select', this.selectHandler, this.scope);
38491     }
38492 };
38493 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38494  * Based on:
38495  * Ext JS Library 1.1.1
38496  * Copyright(c) 2006-2007, Ext JS, LLC.
38497  *
38498  * Originally Released Under LGPL - original licence link has changed is not relivant.
38499  *
38500  * Fork - LGPL
38501  * <script type="text/javascript">
38502  */
38503  
38504
38505 /**
38506  * @class Roo.menu.DateMenu
38507  * @extends Roo.menu.Menu
38508  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38509  * @constructor
38510  * Creates a new DateMenu
38511  * @param {Object} config Configuration options
38512  */
38513 Roo.menu.DateMenu = function(config){
38514     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38515     this.plain = true;
38516     var di = new Roo.menu.DateItem(config);
38517     this.add(di);
38518     /**
38519      * The {@link Roo.DatePicker} instance for this DateMenu
38520      * @type DatePicker
38521      */
38522     this.picker = di.picker;
38523     /**
38524      * @event select
38525      * @param {DatePicker} picker
38526      * @param {Date} date
38527      */
38528     this.relayEvents(di, ["select"]);
38529     this.on('beforeshow', function(){
38530         if(this.picker){
38531             this.picker.hideMonthPicker(false);
38532         }
38533     }, this);
38534 };
38535 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38536     cls:'x-date-menu'
38537 });/*
38538  * Based on:
38539  * Ext JS Library 1.1.1
38540  * Copyright(c) 2006-2007, Ext JS, LLC.
38541  *
38542  * Originally Released Under LGPL - original licence link has changed is not relivant.
38543  *
38544  * Fork - LGPL
38545  * <script type="text/javascript">
38546  */
38547  
38548
38549 /**
38550  * @class Roo.menu.ColorMenu
38551  * @extends Roo.menu.Menu
38552  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38553  * @constructor
38554  * Creates a new ColorMenu
38555  * @param {Object} config Configuration options
38556  */
38557 Roo.menu.ColorMenu = function(config){
38558     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38559     this.plain = true;
38560     var ci = new Roo.menu.ColorItem(config);
38561     this.add(ci);
38562     /**
38563      * The {@link Roo.ColorPalette} instance for this ColorMenu
38564      * @type ColorPalette
38565      */
38566     this.palette = ci.palette;
38567     /**
38568      * @event select
38569      * @param {ColorPalette} palette
38570      * @param {String} color
38571      */
38572     this.relayEvents(ci, ["select"]);
38573 };
38574 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38575  * Based on:
38576  * Ext JS Library 1.1.1
38577  * Copyright(c) 2006-2007, Ext JS, LLC.
38578  *
38579  * Originally Released Under LGPL - original licence link has changed is not relivant.
38580  *
38581  * Fork - LGPL
38582  * <script type="text/javascript">
38583  */
38584  
38585 /**
38586  * @class Roo.form.Field
38587  * @extends Roo.BoxComponent
38588  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38589  * @constructor
38590  * Creates a new Field
38591  * @param {Object} config Configuration options
38592  */
38593 Roo.form.Field = function(config){
38594     Roo.form.Field.superclass.constructor.call(this, config);
38595 };
38596
38597 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38598     /**
38599      * @cfg {String} fieldLabel Label to use when rendering a form.
38600      */
38601        /**
38602      * @cfg {String} qtip Mouse over tip
38603      */
38604      
38605     /**
38606      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38607      */
38608     invalidClass : "x-form-invalid",
38609     /**
38610      * @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")
38611      */
38612     invalidText : "The value in this field is invalid",
38613     /**
38614      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38615      */
38616     focusClass : "x-form-focus",
38617     /**
38618      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38619       automatic validation (defaults to "keyup").
38620      */
38621     validationEvent : "keyup",
38622     /**
38623      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38624      */
38625     validateOnBlur : true,
38626     /**
38627      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38628      */
38629     validationDelay : 250,
38630     /**
38631      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38632      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38633      */
38634     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38635     /**
38636      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38637      */
38638     fieldClass : "x-form-field",
38639     /**
38640      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38641      *<pre>
38642 Value         Description
38643 -----------   ----------------------------------------------------------------------
38644 qtip          Display a quick tip when the user hovers over the field
38645 title         Display a default browser title attribute popup
38646 under         Add a block div beneath the field containing the error text
38647 side          Add an error icon to the right of the field with a popup on hover
38648 [element id]  Add the error text directly to the innerHTML of the specified element
38649 </pre>
38650      */
38651     msgTarget : 'qtip',
38652     /**
38653      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38654      */
38655     msgFx : 'normal',
38656
38657     /**
38658      * @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.
38659      */
38660     readOnly : false,
38661
38662     /**
38663      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38664      */
38665     disabled : false,
38666
38667     /**
38668      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38669      */
38670     inputType : undefined,
38671     
38672     /**
38673      * @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).
38674          */
38675         tabIndex : undefined,
38676         
38677     // private
38678     isFormField : true,
38679
38680     // private
38681     hasFocus : false,
38682     /**
38683      * @property {Roo.Element} fieldEl
38684      * Element Containing the rendered Field (with label etc.)
38685      */
38686     /**
38687      * @cfg {Mixed} value A value to initialize this field with.
38688      */
38689     value : undefined,
38690
38691     /**
38692      * @cfg {String} name The field's HTML name attribute.
38693      */
38694     /**
38695      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38696      */
38697     // private
38698     loadedValue : false,
38699      
38700      
38701         // private ??
38702         initComponent : function(){
38703         Roo.form.Field.superclass.initComponent.call(this);
38704         this.addEvents({
38705             /**
38706              * @event focus
38707              * Fires when this field receives input focus.
38708              * @param {Roo.form.Field} this
38709              */
38710             focus : true,
38711             /**
38712              * @event blur
38713              * Fires when this field loses input focus.
38714              * @param {Roo.form.Field} this
38715              */
38716             blur : true,
38717             /**
38718              * @event specialkey
38719              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38720              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38721              * @param {Roo.form.Field} this
38722              * @param {Roo.EventObject} e The event object
38723              */
38724             specialkey : true,
38725             /**
38726              * @event change
38727              * Fires just before the field blurs if the field value has changed.
38728              * @param {Roo.form.Field} this
38729              * @param {Mixed} newValue The new value
38730              * @param {Mixed} oldValue The original value
38731              */
38732             change : true,
38733             /**
38734              * @event invalid
38735              * Fires after the field has been marked as invalid.
38736              * @param {Roo.form.Field} this
38737              * @param {String} msg The validation message
38738              */
38739             invalid : true,
38740             /**
38741              * @event valid
38742              * Fires after the field has been validated with no errors.
38743              * @param {Roo.form.Field} this
38744              */
38745             valid : true,
38746              /**
38747              * @event keyup
38748              * Fires after the key up
38749              * @param {Roo.form.Field} this
38750              * @param {Roo.EventObject}  e The event Object
38751              */
38752             keyup : true
38753         });
38754     },
38755
38756     /**
38757      * Returns the name attribute of the field if available
38758      * @return {String} name The field name
38759      */
38760     getName: function(){
38761          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38762     },
38763
38764     // private
38765     onRender : function(ct, position){
38766         Roo.form.Field.superclass.onRender.call(this, ct, position);
38767         if(!this.el){
38768             var cfg = this.getAutoCreate();
38769             if(!cfg.name){
38770                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38771             }
38772             if (!cfg.name.length) {
38773                 delete cfg.name;
38774             }
38775             if(this.inputType){
38776                 cfg.type = this.inputType;
38777             }
38778             this.el = ct.createChild(cfg, position);
38779         }
38780         var type = this.el.dom.type;
38781         if(type){
38782             if(type == 'password'){
38783                 type = 'text';
38784             }
38785             this.el.addClass('x-form-'+type);
38786         }
38787         if(this.readOnly){
38788             this.el.dom.readOnly = true;
38789         }
38790         if(this.tabIndex !== undefined){
38791             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38792         }
38793
38794         this.el.addClass([this.fieldClass, this.cls]);
38795         this.initValue();
38796     },
38797
38798     /**
38799      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38800      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38801      * @return {Roo.form.Field} this
38802      */
38803     applyTo : function(target){
38804         this.allowDomMove = false;
38805         this.el = Roo.get(target);
38806         this.render(this.el.dom.parentNode);
38807         return this;
38808     },
38809
38810     // private
38811     initValue : function(){
38812         if(this.value !== undefined){
38813             this.setValue(this.value);
38814         }else if(this.el.dom.value.length > 0){
38815             this.setValue(this.el.dom.value);
38816         }
38817     },
38818
38819     /**
38820      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38821      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38822      */
38823     isDirty : function() {
38824         if(this.disabled) {
38825             return false;
38826         }
38827         return String(this.getValue()) !== String(this.originalValue);
38828     },
38829
38830     /**
38831      * stores the current value in loadedValue
38832      */
38833     resetHasChanged : function()
38834     {
38835         this.loadedValue = String(this.getValue());
38836     },
38837     /**
38838      * checks the current value against the 'loaded' value.
38839      * Note - will return false if 'resetHasChanged' has not been called first.
38840      */
38841     hasChanged : function()
38842     {
38843         if(this.disabled || this.readOnly) {
38844             return false;
38845         }
38846         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38847     },
38848     
38849     
38850     
38851     // private
38852     afterRender : function(){
38853         Roo.form.Field.superclass.afterRender.call(this);
38854         this.initEvents();
38855     },
38856
38857     // private
38858     fireKey : function(e){
38859         //Roo.log('field ' + e.getKey());
38860         if(e.isNavKeyPress()){
38861             this.fireEvent("specialkey", this, e);
38862         }
38863     },
38864
38865     /**
38866      * Resets the current field value to the originally loaded value and clears any validation messages
38867      */
38868     reset : function(){
38869         this.setValue(this.resetValue);
38870         this.clearInvalid();
38871     },
38872
38873     // private
38874     initEvents : function(){
38875         // safari killled keypress - so keydown is now used..
38876         this.el.on("keydown" , this.fireKey,  this);
38877         this.el.on("focus", this.onFocus,  this);
38878         this.el.on("blur", this.onBlur,  this);
38879         this.el.relayEvent('keyup', this);
38880
38881         // reference to original value for reset
38882         this.originalValue = this.getValue();
38883         this.resetValue =  this.getValue();
38884     },
38885
38886     // private
38887     onFocus : function(){
38888         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38889             this.el.addClass(this.focusClass);
38890         }
38891         if(!this.hasFocus){
38892             this.hasFocus = true;
38893             this.startValue = this.getValue();
38894             this.fireEvent("focus", this);
38895         }
38896     },
38897
38898     beforeBlur : Roo.emptyFn,
38899
38900     // private
38901     onBlur : function(){
38902         this.beforeBlur();
38903         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38904             this.el.removeClass(this.focusClass);
38905         }
38906         this.hasFocus = false;
38907         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
38908             this.validate();
38909         }
38910         var v = this.getValue();
38911         if(String(v) !== String(this.startValue)){
38912             this.fireEvent('change', this, v, this.startValue);
38913         }
38914         this.fireEvent("blur", this);
38915     },
38916
38917     /**
38918      * Returns whether or not the field value is currently valid
38919      * @param {Boolean} preventMark True to disable marking the field invalid
38920      * @return {Boolean} True if the value is valid, else false
38921      */
38922     isValid : function(preventMark){
38923         if(this.disabled){
38924             return true;
38925         }
38926         var restore = this.preventMark;
38927         this.preventMark = preventMark === true;
38928         var v = this.validateValue(this.processValue(this.getRawValue()));
38929         this.preventMark = restore;
38930         return v;
38931     },
38932
38933     /**
38934      * Validates the field value
38935      * @return {Boolean} True if the value is valid, else false
38936      */
38937     validate : function(){
38938         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
38939             this.clearInvalid();
38940             return true;
38941         }
38942         return false;
38943     },
38944
38945     processValue : function(value){
38946         return value;
38947     },
38948
38949     // private
38950     // Subclasses should provide the validation implementation by overriding this
38951     validateValue : function(value){
38952         return true;
38953     },
38954
38955     /**
38956      * Mark this field as invalid
38957      * @param {String} msg The validation message
38958      */
38959     markInvalid : function(msg){
38960         if(!this.rendered || this.preventMark){ // not rendered
38961             return;
38962         }
38963         
38964         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38965         
38966         obj.el.addClass(this.invalidClass);
38967         msg = msg || this.invalidText;
38968         switch(this.msgTarget){
38969             case 'qtip':
38970                 obj.el.dom.qtip = msg;
38971                 obj.el.dom.qclass = 'x-form-invalid-tip';
38972                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
38973                     Roo.QuickTips.enable();
38974                 }
38975                 break;
38976             case 'title':
38977                 this.el.dom.title = msg;
38978                 break;
38979             case 'under':
38980                 if(!this.errorEl){
38981                     var elp = this.el.findParent('.x-form-element', 5, true);
38982                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
38983                     this.errorEl.setWidth(elp.getWidth(true)-20);
38984                 }
38985                 this.errorEl.update(msg);
38986                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
38987                 break;
38988             case 'side':
38989                 if(!this.errorIcon){
38990                     var elp = this.el.findParent('.x-form-element', 5, true);
38991                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
38992                 }
38993                 this.alignErrorIcon();
38994                 this.errorIcon.dom.qtip = msg;
38995                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
38996                 this.errorIcon.show();
38997                 this.on('resize', this.alignErrorIcon, this);
38998                 break;
38999             default:
39000                 var t = Roo.getDom(this.msgTarget);
39001                 t.innerHTML = msg;
39002                 t.style.display = this.msgDisplay;
39003                 break;
39004         }
39005         this.fireEvent('invalid', this, msg);
39006     },
39007
39008     // private
39009     alignErrorIcon : function(){
39010         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39011     },
39012
39013     /**
39014      * Clear any invalid styles/messages for this field
39015      */
39016     clearInvalid : function(){
39017         if(!this.rendered || this.preventMark){ // not rendered
39018             return;
39019         }
39020         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39021         
39022         obj.el.removeClass(this.invalidClass);
39023         switch(this.msgTarget){
39024             case 'qtip':
39025                 obj.el.dom.qtip = '';
39026                 break;
39027             case 'title':
39028                 this.el.dom.title = '';
39029                 break;
39030             case 'under':
39031                 if(this.errorEl){
39032                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39033                 }
39034                 break;
39035             case 'side':
39036                 if(this.errorIcon){
39037                     this.errorIcon.dom.qtip = '';
39038                     this.errorIcon.hide();
39039                     this.un('resize', this.alignErrorIcon, this);
39040                 }
39041                 break;
39042             default:
39043                 var t = Roo.getDom(this.msgTarget);
39044                 t.innerHTML = '';
39045                 t.style.display = 'none';
39046                 break;
39047         }
39048         this.fireEvent('valid', this);
39049     },
39050
39051     /**
39052      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39053      * @return {Mixed} value The field value
39054      */
39055     getRawValue : function(){
39056         var v = this.el.getValue();
39057         
39058         return v;
39059     },
39060
39061     /**
39062      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39063      * @return {Mixed} value The field value
39064      */
39065     getValue : function(){
39066         var v = this.el.getValue();
39067          
39068         return v;
39069     },
39070
39071     /**
39072      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39073      * @param {Mixed} value The value to set
39074      */
39075     setRawValue : function(v){
39076         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39077     },
39078
39079     /**
39080      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39081      * @param {Mixed} value The value to set
39082      */
39083     setValue : function(v){
39084         this.value = v;
39085         if(this.rendered){
39086             this.el.dom.value = (v === null || v === undefined ? '' : v);
39087              this.validate();
39088         }
39089     },
39090
39091     adjustSize : function(w, h){
39092         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39093         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39094         return s;
39095     },
39096
39097     adjustWidth : function(tag, w){
39098         tag = tag.toLowerCase();
39099         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39100             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39101                 if(tag == 'input'){
39102                     return w + 2;
39103                 }
39104                 if(tag == 'textarea'){
39105                     return w-2;
39106                 }
39107             }else if(Roo.isOpera){
39108                 if(tag == 'input'){
39109                     return w + 2;
39110                 }
39111                 if(tag == 'textarea'){
39112                     return w-2;
39113                 }
39114             }
39115         }
39116         return w;
39117     }
39118 });
39119
39120
39121 // anything other than normal should be considered experimental
39122 Roo.form.Field.msgFx = {
39123     normal : {
39124         show: function(msgEl, f){
39125             msgEl.setDisplayed('block');
39126         },
39127
39128         hide : function(msgEl, f){
39129             msgEl.setDisplayed(false).update('');
39130         }
39131     },
39132
39133     slide : {
39134         show: function(msgEl, f){
39135             msgEl.slideIn('t', {stopFx:true});
39136         },
39137
39138         hide : function(msgEl, f){
39139             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39140         }
39141     },
39142
39143     slideRight : {
39144         show: function(msgEl, f){
39145             msgEl.fixDisplay();
39146             msgEl.alignTo(f.el, 'tl-tr');
39147             msgEl.slideIn('l', {stopFx:true});
39148         },
39149
39150         hide : function(msgEl, f){
39151             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39152         }
39153     }
39154 };/*
39155  * Based on:
39156  * Ext JS Library 1.1.1
39157  * Copyright(c) 2006-2007, Ext JS, LLC.
39158  *
39159  * Originally Released Under LGPL - original licence link has changed is not relivant.
39160  *
39161  * Fork - LGPL
39162  * <script type="text/javascript">
39163  */
39164  
39165
39166 /**
39167  * @class Roo.form.TextField
39168  * @extends Roo.form.Field
39169  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39170  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39171  * @constructor
39172  * Creates a new TextField
39173  * @param {Object} config Configuration options
39174  */
39175 Roo.form.TextField = function(config){
39176     Roo.form.TextField.superclass.constructor.call(this, config);
39177     this.addEvents({
39178         /**
39179          * @event autosize
39180          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39181          * according to the default logic, but this event provides a hook for the developer to apply additional
39182          * logic at runtime to resize the field if needed.
39183              * @param {Roo.form.Field} this This text field
39184              * @param {Number} width The new field width
39185              */
39186         autosize : true
39187     });
39188 };
39189
39190 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39191     /**
39192      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39193      */
39194     grow : false,
39195     /**
39196      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39197      */
39198     growMin : 30,
39199     /**
39200      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39201      */
39202     growMax : 800,
39203     /**
39204      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39205      */
39206     vtype : null,
39207     /**
39208      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39209      */
39210     maskRe : null,
39211     /**
39212      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39213      */
39214     disableKeyFilter : false,
39215     /**
39216      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39217      */
39218     allowBlank : true,
39219     /**
39220      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39221      */
39222     minLength : 0,
39223     /**
39224      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39225      */
39226     maxLength : Number.MAX_VALUE,
39227     /**
39228      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39229      */
39230     minLengthText : "The minimum length for this field is {0}",
39231     /**
39232      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39233      */
39234     maxLengthText : "The maximum length for this field is {0}",
39235     /**
39236      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39237      */
39238     selectOnFocus : false,
39239     /**
39240      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39241      */
39242     blankText : "This field is required",
39243     /**
39244      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39245      * If available, this function will be called only after the basic validators all return true, and will be passed the
39246      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39247      */
39248     validator : null,
39249     /**
39250      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39251      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39252      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39253      */
39254     regex : null,
39255     /**
39256      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39257      */
39258     regexText : "",
39259     /**
39260      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39261      */
39262     emptyText : null,
39263    
39264
39265     // private
39266     initEvents : function()
39267     {
39268         if (this.emptyText) {
39269             this.el.attr('placeholder', this.emptyText);
39270         }
39271         
39272         Roo.form.TextField.superclass.initEvents.call(this);
39273         if(this.validationEvent == 'keyup'){
39274             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39275             this.el.on('keyup', this.filterValidation, this);
39276         }
39277         else if(this.validationEvent !== false){
39278             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39279         }
39280         
39281         if(this.selectOnFocus){
39282             this.on("focus", this.preFocus, this);
39283             
39284         }
39285         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39286             this.el.on("keypress", this.filterKeys, this);
39287         }
39288         if(this.grow){
39289             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39290             this.el.on("click", this.autoSize,  this);
39291         }
39292         if(this.el.is('input[type=password]') && Roo.isSafari){
39293             this.el.on('keydown', this.SafariOnKeyDown, this);
39294         }
39295     },
39296
39297     processValue : function(value){
39298         if(this.stripCharsRe){
39299             var newValue = value.replace(this.stripCharsRe, '');
39300             if(newValue !== value){
39301                 this.setRawValue(newValue);
39302                 return newValue;
39303             }
39304         }
39305         return value;
39306     },
39307
39308     filterValidation : function(e){
39309         if(!e.isNavKeyPress()){
39310             this.validationTask.delay(this.validationDelay);
39311         }
39312     },
39313
39314     // private
39315     onKeyUp : function(e){
39316         if(!e.isNavKeyPress()){
39317             this.autoSize();
39318         }
39319     },
39320
39321     /**
39322      * Resets the current field value to the originally-loaded value and clears any validation messages.
39323      *  
39324      */
39325     reset : function(){
39326         Roo.form.TextField.superclass.reset.call(this);
39327        
39328     },
39329
39330     
39331     // private
39332     preFocus : function(){
39333         
39334         if(this.selectOnFocus){
39335             this.el.dom.select();
39336         }
39337     },
39338
39339     
39340     // private
39341     filterKeys : function(e){
39342         var k = e.getKey();
39343         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39344             return;
39345         }
39346         var c = e.getCharCode(), cc = String.fromCharCode(c);
39347         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39348             return;
39349         }
39350         if(!this.maskRe.test(cc)){
39351             e.stopEvent();
39352         }
39353     },
39354
39355     setValue : function(v){
39356         
39357         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39358         
39359         this.autoSize();
39360     },
39361
39362     /**
39363      * Validates a value according to the field's validation rules and marks the field as invalid
39364      * if the validation fails
39365      * @param {Mixed} value The value to validate
39366      * @return {Boolean} True if the value is valid, else false
39367      */
39368     validateValue : function(value){
39369         if(value.length < 1)  { // if it's blank
39370              if(this.allowBlank){
39371                 this.clearInvalid();
39372                 return true;
39373              }else{
39374                 this.markInvalid(this.blankText);
39375                 return false;
39376              }
39377         }
39378         if(value.length < this.minLength){
39379             this.markInvalid(String.format(this.minLengthText, this.minLength));
39380             return false;
39381         }
39382         if(value.length > this.maxLength){
39383             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39384             return false;
39385         }
39386         if(this.vtype){
39387             var vt = Roo.form.VTypes;
39388             if(!vt[this.vtype](value, this)){
39389                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39390                 return false;
39391             }
39392         }
39393         if(typeof this.validator == "function"){
39394             var msg = this.validator(value);
39395             if(msg !== true){
39396                 this.markInvalid(msg);
39397                 return false;
39398             }
39399         }
39400         if(this.regex && !this.regex.test(value)){
39401             this.markInvalid(this.regexText);
39402             return false;
39403         }
39404         return true;
39405     },
39406
39407     /**
39408      * Selects text in this field
39409      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39410      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39411      */
39412     selectText : function(start, end){
39413         var v = this.getRawValue();
39414         if(v.length > 0){
39415             start = start === undefined ? 0 : start;
39416             end = end === undefined ? v.length : end;
39417             var d = this.el.dom;
39418             if(d.setSelectionRange){
39419                 d.setSelectionRange(start, end);
39420             }else if(d.createTextRange){
39421                 var range = d.createTextRange();
39422                 range.moveStart("character", start);
39423                 range.moveEnd("character", v.length-end);
39424                 range.select();
39425             }
39426         }
39427     },
39428
39429     /**
39430      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39431      * This only takes effect if grow = true, and fires the autosize event.
39432      */
39433     autoSize : function(){
39434         if(!this.grow || !this.rendered){
39435             return;
39436         }
39437         if(!this.metrics){
39438             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39439         }
39440         var el = this.el;
39441         var v = el.dom.value;
39442         var d = document.createElement('div');
39443         d.appendChild(document.createTextNode(v));
39444         v = d.innerHTML;
39445         d = null;
39446         v += "&#160;";
39447         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39448         this.el.setWidth(w);
39449         this.fireEvent("autosize", this, w);
39450     },
39451     
39452     // private
39453     SafariOnKeyDown : function(event)
39454     {
39455         // this is a workaround for a password hang bug on chrome/ webkit.
39456         
39457         var isSelectAll = false;
39458         
39459         if(this.el.dom.selectionEnd > 0){
39460             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39461         }
39462         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39463             event.preventDefault();
39464             this.setValue('');
39465             return;
39466         }
39467         
39468         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39469             
39470             event.preventDefault();
39471             // this is very hacky as keydown always get's upper case.
39472             
39473             var cc = String.fromCharCode(event.getCharCode());
39474             
39475             
39476             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39477             
39478         }
39479         
39480         
39481     }
39482 });/*
39483  * Based on:
39484  * Ext JS Library 1.1.1
39485  * Copyright(c) 2006-2007, Ext JS, LLC.
39486  *
39487  * Originally Released Under LGPL - original licence link has changed is not relivant.
39488  *
39489  * Fork - LGPL
39490  * <script type="text/javascript">
39491  */
39492  
39493 /**
39494  * @class Roo.form.Hidden
39495  * @extends Roo.form.TextField
39496  * Simple Hidden element used on forms 
39497  * 
39498  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39499  * 
39500  * @constructor
39501  * Creates a new Hidden form element.
39502  * @param {Object} config Configuration options
39503  */
39504
39505
39506
39507 // easy hidden field...
39508 Roo.form.Hidden = function(config){
39509     Roo.form.Hidden.superclass.constructor.call(this, config);
39510 };
39511   
39512 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39513     fieldLabel:      '',
39514     inputType:      'hidden',
39515     width:          50,
39516     allowBlank:     true,
39517     labelSeparator: '',
39518     hidden:         true,
39519     itemCls :       'x-form-item-display-none'
39520
39521
39522 });
39523
39524
39525 /*
39526  * Based on:
39527  * Ext JS Library 1.1.1
39528  * Copyright(c) 2006-2007, Ext JS, LLC.
39529  *
39530  * Originally Released Under LGPL - original licence link has changed is not relivant.
39531  *
39532  * Fork - LGPL
39533  * <script type="text/javascript">
39534  */
39535  
39536 /**
39537  * @class Roo.form.TriggerField
39538  * @extends Roo.form.TextField
39539  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39540  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39541  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39542  * for which you can provide a custom implementation.  For example:
39543  * <pre><code>
39544 var trigger = new Roo.form.TriggerField();
39545 trigger.onTriggerClick = myTriggerFn;
39546 trigger.applyTo('my-field');
39547 </code></pre>
39548  *
39549  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39550  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39551  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39552  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39553  * @constructor
39554  * Create a new TriggerField.
39555  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39556  * to the base TextField)
39557  */
39558 Roo.form.TriggerField = function(config){
39559     this.mimicing = false;
39560     Roo.form.TriggerField.superclass.constructor.call(this, config);
39561 };
39562
39563 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39564     /**
39565      * @cfg {String} triggerClass A CSS class to apply to the trigger
39566      */
39567     /**
39568      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39569      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39570      */
39571     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39572     /**
39573      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39574      */
39575     hideTrigger:false,
39576
39577     /** @cfg {Boolean} grow @hide */
39578     /** @cfg {Number} growMin @hide */
39579     /** @cfg {Number} growMax @hide */
39580
39581     /**
39582      * @hide 
39583      * @method
39584      */
39585     autoSize: Roo.emptyFn,
39586     // private
39587     monitorTab : true,
39588     // private
39589     deferHeight : true,
39590
39591     
39592     actionMode : 'wrap',
39593     // private
39594     onResize : function(w, h){
39595         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39596         if(typeof w == 'number'){
39597             var x = w - this.trigger.getWidth();
39598             this.el.setWidth(this.adjustWidth('input', x));
39599             this.trigger.setStyle('left', x+'px');
39600         }
39601     },
39602
39603     // private
39604     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39605
39606     // private
39607     getResizeEl : function(){
39608         return this.wrap;
39609     },
39610
39611     // private
39612     getPositionEl : function(){
39613         return this.wrap;
39614     },
39615
39616     // private
39617     alignErrorIcon : function(){
39618         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39619     },
39620
39621     // private
39622     onRender : function(ct, position){
39623         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39624         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39625         this.trigger = this.wrap.createChild(this.triggerConfig ||
39626                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39627         if(this.hideTrigger){
39628             this.trigger.setDisplayed(false);
39629         }
39630         this.initTrigger();
39631         if(!this.width){
39632             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39633         }
39634     },
39635
39636     // private
39637     initTrigger : function(){
39638         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39639         this.trigger.addClassOnOver('x-form-trigger-over');
39640         this.trigger.addClassOnClick('x-form-trigger-click');
39641     },
39642
39643     // private
39644     onDestroy : function(){
39645         if(this.trigger){
39646             this.trigger.removeAllListeners();
39647             this.trigger.remove();
39648         }
39649         if(this.wrap){
39650             this.wrap.remove();
39651         }
39652         Roo.form.TriggerField.superclass.onDestroy.call(this);
39653     },
39654
39655     // private
39656     onFocus : function(){
39657         Roo.form.TriggerField.superclass.onFocus.call(this);
39658         if(!this.mimicing){
39659             this.wrap.addClass('x-trigger-wrap-focus');
39660             this.mimicing = true;
39661             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39662             if(this.monitorTab){
39663                 this.el.on("keydown", this.checkTab, this);
39664             }
39665         }
39666     },
39667
39668     // private
39669     checkTab : function(e){
39670         if(e.getKey() == e.TAB){
39671             this.triggerBlur();
39672         }
39673     },
39674
39675     // private
39676     onBlur : function(){
39677         // do nothing
39678     },
39679
39680     // private
39681     mimicBlur : function(e, t){
39682         if(!this.wrap.contains(t) && this.validateBlur()){
39683             this.triggerBlur();
39684         }
39685     },
39686
39687     // private
39688     triggerBlur : function(){
39689         this.mimicing = false;
39690         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39691         if(this.monitorTab){
39692             this.el.un("keydown", this.checkTab, this);
39693         }
39694         this.wrap.removeClass('x-trigger-wrap-focus');
39695         Roo.form.TriggerField.superclass.onBlur.call(this);
39696     },
39697
39698     // private
39699     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39700     validateBlur : function(e, t){
39701         return true;
39702     },
39703
39704     // private
39705     onDisable : function(){
39706         Roo.form.TriggerField.superclass.onDisable.call(this);
39707         if(this.wrap){
39708             this.wrap.addClass('x-item-disabled');
39709         }
39710     },
39711
39712     // private
39713     onEnable : function(){
39714         Roo.form.TriggerField.superclass.onEnable.call(this);
39715         if(this.wrap){
39716             this.wrap.removeClass('x-item-disabled');
39717         }
39718     },
39719
39720     // private
39721     onShow : function(){
39722         var ae = this.getActionEl();
39723         
39724         if(ae){
39725             ae.dom.style.display = '';
39726             ae.dom.style.visibility = 'visible';
39727         }
39728     },
39729
39730     // private
39731     
39732     onHide : function(){
39733         var ae = this.getActionEl();
39734         ae.dom.style.display = 'none';
39735     },
39736
39737     /**
39738      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39739      * by an implementing function.
39740      * @method
39741      * @param {EventObject} e
39742      */
39743     onTriggerClick : Roo.emptyFn
39744 });
39745
39746 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39747 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39748 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39749 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39750     initComponent : function(){
39751         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39752
39753         this.triggerConfig = {
39754             tag:'span', cls:'x-form-twin-triggers', cn:[
39755             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39756             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39757         ]};
39758     },
39759
39760     getTrigger : function(index){
39761         return this.triggers[index];
39762     },
39763
39764     initTrigger : function(){
39765         var ts = this.trigger.select('.x-form-trigger', true);
39766         this.wrap.setStyle('overflow', 'hidden');
39767         var triggerField = this;
39768         ts.each(function(t, all, index){
39769             t.hide = function(){
39770                 var w = triggerField.wrap.getWidth();
39771                 this.dom.style.display = 'none';
39772                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39773             };
39774             t.show = function(){
39775                 var w = triggerField.wrap.getWidth();
39776                 this.dom.style.display = '';
39777                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39778             };
39779             var triggerIndex = 'Trigger'+(index+1);
39780
39781             if(this['hide'+triggerIndex]){
39782                 t.dom.style.display = 'none';
39783             }
39784             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39785             t.addClassOnOver('x-form-trigger-over');
39786             t.addClassOnClick('x-form-trigger-click');
39787         }, this);
39788         this.triggers = ts.elements;
39789     },
39790
39791     onTrigger1Click : Roo.emptyFn,
39792     onTrigger2Click : Roo.emptyFn
39793 });/*
39794  * Based on:
39795  * Ext JS Library 1.1.1
39796  * Copyright(c) 2006-2007, Ext JS, LLC.
39797  *
39798  * Originally Released Under LGPL - original licence link has changed is not relivant.
39799  *
39800  * Fork - LGPL
39801  * <script type="text/javascript">
39802  */
39803  
39804 /**
39805  * @class Roo.form.TextArea
39806  * @extends Roo.form.TextField
39807  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39808  * support for auto-sizing.
39809  * @constructor
39810  * Creates a new TextArea
39811  * @param {Object} config Configuration options
39812  */
39813 Roo.form.TextArea = function(config){
39814     Roo.form.TextArea.superclass.constructor.call(this, config);
39815     // these are provided exchanges for backwards compat
39816     // minHeight/maxHeight were replaced by growMin/growMax to be
39817     // compatible with TextField growing config values
39818     if(this.minHeight !== undefined){
39819         this.growMin = this.minHeight;
39820     }
39821     if(this.maxHeight !== undefined){
39822         this.growMax = this.maxHeight;
39823     }
39824 };
39825
39826 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39827     /**
39828      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39829      */
39830     growMin : 60,
39831     /**
39832      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39833      */
39834     growMax: 1000,
39835     /**
39836      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39837      * in the field (equivalent to setting overflow: hidden, defaults to false)
39838      */
39839     preventScrollbars: false,
39840     /**
39841      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39842      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39843      */
39844
39845     // private
39846     onRender : function(ct, position){
39847         if(!this.el){
39848             this.defaultAutoCreate = {
39849                 tag: "textarea",
39850                 style:"width:300px;height:60px;",
39851                 autocomplete: "new-password"
39852             };
39853         }
39854         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39855         if(this.grow){
39856             this.textSizeEl = Roo.DomHelper.append(document.body, {
39857                 tag: "pre", cls: "x-form-grow-sizer"
39858             });
39859             if(this.preventScrollbars){
39860                 this.el.setStyle("overflow", "hidden");
39861             }
39862             this.el.setHeight(this.growMin);
39863         }
39864     },
39865
39866     onDestroy : function(){
39867         if(this.textSizeEl){
39868             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39869         }
39870         Roo.form.TextArea.superclass.onDestroy.call(this);
39871     },
39872
39873     // private
39874     onKeyUp : function(e){
39875         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39876             this.autoSize();
39877         }
39878     },
39879
39880     /**
39881      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39882      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39883      */
39884     autoSize : function(){
39885         if(!this.grow || !this.textSizeEl){
39886             return;
39887         }
39888         var el = this.el;
39889         var v = el.dom.value;
39890         var ts = this.textSizeEl;
39891
39892         ts.innerHTML = '';
39893         ts.appendChild(document.createTextNode(v));
39894         v = ts.innerHTML;
39895
39896         Roo.fly(ts).setWidth(this.el.getWidth());
39897         if(v.length < 1){
39898             v = "&#160;&#160;";
39899         }else{
39900             if(Roo.isIE){
39901                 v = v.replace(/\n/g, '<p>&#160;</p>');
39902             }
39903             v += "&#160;\n&#160;";
39904         }
39905         ts.innerHTML = v;
39906         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
39907         if(h != this.lastHeight){
39908             this.lastHeight = h;
39909             this.el.setHeight(h);
39910             this.fireEvent("autosize", this, h);
39911         }
39912     }
39913 });/*
39914  * Based on:
39915  * Ext JS Library 1.1.1
39916  * Copyright(c) 2006-2007, Ext JS, LLC.
39917  *
39918  * Originally Released Under LGPL - original licence link has changed is not relivant.
39919  *
39920  * Fork - LGPL
39921  * <script type="text/javascript">
39922  */
39923  
39924
39925 /**
39926  * @class Roo.form.NumberField
39927  * @extends Roo.form.TextField
39928  * Numeric text field that provides automatic keystroke filtering and numeric validation.
39929  * @constructor
39930  * Creates a new NumberField
39931  * @param {Object} config Configuration options
39932  */
39933 Roo.form.NumberField = function(config){
39934     Roo.form.NumberField.superclass.constructor.call(this, config);
39935 };
39936
39937 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
39938     /**
39939      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
39940      */
39941     fieldClass: "x-form-field x-form-num-field",
39942     /**
39943      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39944      */
39945     allowDecimals : true,
39946     /**
39947      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39948      */
39949     decimalSeparator : ".",
39950     /**
39951      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39952      */
39953     decimalPrecision : 2,
39954     /**
39955      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39956      */
39957     allowNegative : true,
39958     /**
39959      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39960      */
39961     minValue : Number.NEGATIVE_INFINITY,
39962     /**
39963      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39964      */
39965     maxValue : Number.MAX_VALUE,
39966     /**
39967      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39968      */
39969     minText : "The minimum value for this field is {0}",
39970     /**
39971      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39972      */
39973     maxText : "The maximum value for this field is {0}",
39974     /**
39975      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39976      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39977      */
39978     nanText : "{0} is not a valid number",
39979
39980     // private
39981     initEvents : function(){
39982         Roo.form.NumberField.superclass.initEvents.call(this);
39983         var allowed = "0123456789";
39984         if(this.allowDecimals){
39985             allowed += this.decimalSeparator;
39986         }
39987         if(this.allowNegative){
39988             allowed += "-";
39989         }
39990         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39991         var keyPress = function(e){
39992             var k = e.getKey();
39993             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39994                 return;
39995             }
39996             var c = e.getCharCode();
39997             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39998                 e.stopEvent();
39999             }
40000         };
40001         this.el.on("keypress", keyPress, this);
40002     },
40003
40004     // private
40005     validateValue : function(value){
40006         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40007             return false;
40008         }
40009         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40010              return true;
40011         }
40012         var num = this.parseValue(value);
40013         if(isNaN(num)){
40014             this.markInvalid(String.format(this.nanText, value));
40015             return false;
40016         }
40017         if(num < this.minValue){
40018             this.markInvalid(String.format(this.minText, this.minValue));
40019             return false;
40020         }
40021         if(num > this.maxValue){
40022             this.markInvalid(String.format(this.maxText, this.maxValue));
40023             return false;
40024         }
40025         return true;
40026     },
40027
40028     getValue : function(){
40029         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40030     },
40031
40032     // private
40033     parseValue : function(value){
40034         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40035         return isNaN(value) ? '' : value;
40036     },
40037
40038     // private
40039     fixPrecision : function(value){
40040         var nan = isNaN(value);
40041         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40042             return nan ? '' : value;
40043         }
40044         return parseFloat(value).toFixed(this.decimalPrecision);
40045     },
40046
40047     setValue : function(v){
40048         v = this.fixPrecision(v);
40049         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40050     },
40051
40052     // private
40053     decimalPrecisionFcn : function(v){
40054         return Math.floor(v);
40055     },
40056
40057     beforeBlur : function(){
40058         var v = this.parseValue(this.getRawValue());
40059         if(v){
40060             this.setValue(v);
40061         }
40062     }
40063 });/*
40064  * Based on:
40065  * Ext JS Library 1.1.1
40066  * Copyright(c) 2006-2007, Ext JS, LLC.
40067  *
40068  * Originally Released Under LGPL - original licence link has changed is not relivant.
40069  *
40070  * Fork - LGPL
40071  * <script type="text/javascript">
40072  */
40073  
40074 /**
40075  * @class Roo.form.DateField
40076  * @extends Roo.form.TriggerField
40077  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40078 * @constructor
40079 * Create a new DateField
40080 * @param {Object} config
40081  */
40082 Roo.form.DateField = function(config){
40083     Roo.form.DateField.superclass.constructor.call(this, config);
40084     
40085       this.addEvents({
40086          
40087         /**
40088          * @event select
40089          * Fires when a date is selected
40090              * @param {Roo.form.DateField} combo This combo box
40091              * @param {Date} date The date selected
40092              */
40093         'select' : true
40094          
40095     });
40096     
40097     
40098     if(typeof this.minValue == "string") {
40099         this.minValue = this.parseDate(this.minValue);
40100     }
40101     if(typeof this.maxValue == "string") {
40102         this.maxValue = this.parseDate(this.maxValue);
40103     }
40104     this.ddMatch = null;
40105     if(this.disabledDates){
40106         var dd = this.disabledDates;
40107         var re = "(?:";
40108         for(var i = 0; i < dd.length; i++){
40109             re += dd[i];
40110             if(i != dd.length-1) {
40111                 re += "|";
40112             }
40113         }
40114         this.ddMatch = new RegExp(re + ")");
40115     }
40116 };
40117
40118 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40119     /**
40120      * @cfg {String} format
40121      * The default date format string which can be overriden for localization support.  The format must be
40122      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40123      */
40124     format : "m/d/y",
40125     /**
40126      * @cfg {String} altFormats
40127      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40128      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40129      */
40130     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40131     /**
40132      * @cfg {Array} disabledDays
40133      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40134      */
40135     disabledDays : null,
40136     /**
40137      * @cfg {String} disabledDaysText
40138      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40139      */
40140     disabledDaysText : "Disabled",
40141     /**
40142      * @cfg {Array} disabledDates
40143      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40144      * expression so they are very powerful. Some examples:
40145      * <ul>
40146      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40147      * <li>["03/08", "09/16"] would disable those days for every year</li>
40148      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40149      * <li>["03/../2006"] would disable every day in March 2006</li>
40150      * <li>["^03"] would disable every day in every March</li>
40151      * </ul>
40152      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40153      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40154      */
40155     disabledDates : null,
40156     /**
40157      * @cfg {String} disabledDatesText
40158      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40159      */
40160     disabledDatesText : "Disabled",
40161     /**
40162      * @cfg {Date/String} minValue
40163      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40164      * valid format (defaults to null).
40165      */
40166     minValue : null,
40167     /**
40168      * @cfg {Date/String} maxValue
40169      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40170      * valid format (defaults to null).
40171      */
40172     maxValue : null,
40173     /**
40174      * @cfg {String} minText
40175      * The error text to display when the date in the cell is before minValue (defaults to
40176      * 'The date in this field must be after {minValue}').
40177      */
40178     minText : "The date in this field must be equal to or after {0}",
40179     /**
40180      * @cfg {String} maxText
40181      * The error text to display when the date in the cell is after maxValue (defaults to
40182      * 'The date in this field must be before {maxValue}').
40183      */
40184     maxText : "The date in this field must be equal to or before {0}",
40185     /**
40186      * @cfg {String} invalidText
40187      * The error text to display when the date in the field is invalid (defaults to
40188      * '{value} is not a valid date - it must be in the format {format}').
40189      */
40190     invalidText : "{0} is not a valid date - it must be in the format {1}",
40191     /**
40192      * @cfg {String} triggerClass
40193      * An additional CSS class used to style the trigger button.  The trigger will always get the
40194      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40195      * which displays a calendar icon).
40196      */
40197     triggerClass : 'x-form-date-trigger',
40198     
40199
40200     /**
40201      * @cfg {Boolean} useIso
40202      * if enabled, then the date field will use a hidden field to store the 
40203      * real value as iso formated date. default (false)
40204      */ 
40205     useIso : false,
40206     /**
40207      * @cfg {String/Object} autoCreate
40208      * A DomHelper element spec, or true for a default element spec (defaults to
40209      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40210      */ 
40211     // private
40212     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40213     
40214     // private
40215     hiddenField: false,
40216     
40217     onRender : function(ct, position)
40218     {
40219         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40220         if (this.useIso) {
40221             //this.el.dom.removeAttribute('name'); 
40222             Roo.log("Changing name?");
40223             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40224             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40225                     'before', true);
40226             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40227             // prevent input submission
40228             this.hiddenName = this.name;
40229         }
40230             
40231             
40232     },
40233     
40234     // private
40235     validateValue : function(value)
40236     {
40237         value = this.formatDate(value);
40238         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40239             Roo.log('super failed');
40240             return false;
40241         }
40242         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40243              return true;
40244         }
40245         var svalue = value;
40246         value = this.parseDate(value);
40247         if(!value){
40248             Roo.log('parse date failed' + svalue);
40249             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40250             return false;
40251         }
40252         var time = value.getTime();
40253         if(this.minValue && time < this.minValue.getTime()){
40254             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40255             return false;
40256         }
40257         if(this.maxValue && time > this.maxValue.getTime()){
40258             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40259             return false;
40260         }
40261         if(this.disabledDays){
40262             var day = value.getDay();
40263             for(var i = 0; i < this.disabledDays.length; i++) {
40264                 if(day === this.disabledDays[i]){
40265                     this.markInvalid(this.disabledDaysText);
40266                     return false;
40267                 }
40268             }
40269         }
40270         var fvalue = this.formatDate(value);
40271         if(this.ddMatch && this.ddMatch.test(fvalue)){
40272             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40273             return false;
40274         }
40275         return true;
40276     },
40277
40278     // private
40279     // Provides logic to override the default TriggerField.validateBlur which just returns true
40280     validateBlur : function(){
40281         return !this.menu || !this.menu.isVisible();
40282     },
40283     
40284     getName: function()
40285     {
40286         // returns hidden if it's set..
40287         if (!this.rendered) {return ''};
40288         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40289         
40290     },
40291
40292     /**
40293      * Returns the current date value of the date field.
40294      * @return {Date} The date value
40295      */
40296     getValue : function(){
40297         
40298         return  this.hiddenField ?
40299                 this.hiddenField.value :
40300                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40301     },
40302
40303     /**
40304      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40305      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40306      * (the default format used is "m/d/y").
40307      * <br />Usage:
40308      * <pre><code>
40309 //All of these calls set the same date value (May 4, 2006)
40310
40311 //Pass a date object:
40312 var dt = new Date('5/4/06');
40313 dateField.setValue(dt);
40314
40315 //Pass a date string (default format):
40316 dateField.setValue('5/4/06');
40317
40318 //Pass a date string (custom format):
40319 dateField.format = 'Y-m-d';
40320 dateField.setValue('2006-5-4');
40321 </code></pre>
40322      * @param {String/Date} date The date or valid date string
40323      */
40324     setValue : function(date){
40325         if (this.hiddenField) {
40326             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40327         }
40328         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40329         // make sure the value field is always stored as a date..
40330         this.value = this.parseDate(date);
40331         
40332         
40333     },
40334
40335     // private
40336     parseDate : function(value){
40337         if(!value || value instanceof Date){
40338             return value;
40339         }
40340         var v = Date.parseDate(value, this.format);
40341          if (!v && this.useIso) {
40342             v = Date.parseDate(value, 'Y-m-d');
40343         }
40344         if(!v && this.altFormats){
40345             if(!this.altFormatsArray){
40346                 this.altFormatsArray = this.altFormats.split("|");
40347             }
40348             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40349                 v = Date.parseDate(value, this.altFormatsArray[i]);
40350             }
40351         }
40352         return v;
40353     },
40354
40355     // private
40356     formatDate : function(date, fmt){
40357         return (!date || !(date instanceof Date)) ?
40358                date : date.dateFormat(fmt || this.format);
40359     },
40360
40361     // private
40362     menuListeners : {
40363         select: function(m, d){
40364             
40365             this.setValue(d);
40366             this.fireEvent('select', this, d);
40367         },
40368         show : function(){ // retain focus styling
40369             this.onFocus();
40370         },
40371         hide : function(){
40372             this.focus.defer(10, this);
40373             var ml = this.menuListeners;
40374             this.menu.un("select", ml.select,  this);
40375             this.menu.un("show", ml.show,  this);
40376             this.menu.un("hide", ml.hide,  this);
40377         }
40378     },
40379
40380     // private
40381     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40382     onTriggerClick : function(){
40383         if(this.disabled){
40384             return;
40385         }
40386         if(this.menu == null){
40387             this.menu = new Roo.menu.DateMenu();
40388         }
40389         Roo.apply(this.menu.picker,  {
40390             showClear: this.allowBlank,
40391             minDate : this.minValue,
40392             maxDate : this.maxValue,
40393             disabledDatesRE : this.ddMatch,
40394             disabledDatesText : this.disabledDatesText,
40395             disabledDays : this.disabledDays,
40396             disabledDaysText : this.disabledDaysText,
40397             format : this.useIso ? 'Y-m-d' : this.format,
40398             minText : String.format(this.minText, this.formatDate(this.minValue)),
40399             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40400         });
40401         this.menu.on(Roo.apply({}, this.menuListeners, {
40402             scope:this
40403         }));
40404         this.menu.picker.setValue(this.getValue() || new Date());
40405         this.menu.show(this.el, "tl-bl?");
40406     },
40407
40408     beforeBlur : function(){
40409         var v = this.parseDate(this.getRawValue());
40410         if(v){
40411             this.setValue(v);
40412         }
40413     },
40414
40415     /*@
40416      * overide
40417      * 
40418      */
40419     isDirty : function() {
40420         if(this.disabled) {
40421             return false;
40422         }
40423         
40424         if(typeof(this.startValue) === 'undefined'){
40425             return false;
40426         }
40427         
40428         return String(this.getValue()) !== String(this.startValue);
40429         
40430     }
40431 });/*
40432  * Based on:
40433  * Ext JS Library 1.1.1
40434  * Copyright(c) 2006-2007, Ext JS, LLC.
40435  *
40436  * Originally Released Under LGPL - original licence link has changed is not relivant.
40437  *
40438  * Fork - LGPL
40439  * <script type="text/javascript">
40440  */
40441  
40442 /**
40443  * @class Roo.form.MonthField
40444  * @extends Roo.form.TriggerField
40445  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40446 * @constructor
40447 * Create a new MonthField
40448 * @param {Object} config
40449  */
40450 Roo.form.MonthField = function(config){
40451     
40452     Roo.form.MonthField.superclass.constructor.call(this, config);
40453     
40454       this.addEvents({
40455          
40456         /**
40457          * @event select
40458          * Fires when a date is selected
40459              * @param {Roo.form.MonthFieeld} combo This combo box
40460              * @param {Date} date The date selected
40461              */
40462         'select' : true
40463          
40464     });
40465     
40466     
40467     if(typeof this.minValue == "string") {
40468         this.minValue = this.parseDate(this.minValue);
40469     }
40470     if(typeof this.maxValue == "string") {
40471         this.maxValue = this.parseDate(this.maxValue);
40472     }
40473     this.ddMatch = null;
40474     if(this.disabledDates){
40475         var dd = this.disabledDates;
40476         var re = "(?:";
40477         for(var i = 0; i < dd.length; i++){
40478             re += dd[i];
40479             if(i != dd.length-1) {
40480                 re += "|";
40481             }
40482         }
40483         this.ddMatch = new RegExp(re + ")");
40484     }
40485 };
40486
40487 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40488     /**
40489      * @cfg {String} format
40490      * The default date format string which can be overriden for localization support.  The format must be
40491      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40492      */
40493     format : "M Y",
40494     /**
40495      * @cfg {String} altFormats
40496      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40497      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40498      */
40499     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40500     /**
40501      * @cfg {Array} disabledDays
40502      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40503      */
40504     disabledDays : [0,1,2,3,4,5,6],
40505     /**
40506      * @cfg {String} disabledDaysText
40507      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40508      */
40509     disabledDaysText : "Disabled",
40510     /**
40511      * @cfg {Array} disabledDates
40512      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40513      * expression so they are very powerful. Some examples:
40514      * <ul>
40515      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40516      * <li>["03/08", "09/16"] would disable those days for every year</li>
40517      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40518      * <li>["03/../2006"] would disable every day in March 2006</li>
40519      * <li>["^03"] would disable every day in every March</li>
40520      * </ul>
40521      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40522      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40523      */
40524     disabledDates : null,
40525     /**
40526      * @cfg {String} disabledDatesText
40527      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40528      */
40529     disabledDatesText : "Disabled",
40530     /**
40531      * @cfg {Date/String} minValue
40532      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40533      * valid format (defaults to null).
40534      */
40535     minValue : null,
40536     /**
40537      * @cfg {Date/String} maxValue
40538      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40539      * valid format (defaults to null).
40540      */
40541     maxValue : null,
40542     /**
40543      * @cfg {String} minText
40544      * The error text to display when the date in the cell is before minValue (defaults to
40545      * 'The date in this field must be after {minValue}').
40546      */
40547     minText : "The date in this field must be equal to or after {0}",
40548     /**
40549      * @cfg {String} maxTextf
40550      * The error text to display when the date in the cell is after maxValue (defaults to
40551      * 'The date in this field must be before {maxValue}').
40552      */
40553     maxText : "The date in this field must be equal to or before {0}",
40554     /**
40555      * @cfg {String} invalidText
40556      * The error text to display when the date in the field is invalid (defaults to
40557      * '{value} is not a valid date - it must be in the format {format}').
40558      */
40559     invalidText : "{0} is not a valid date - it must be in the format {1}",
40560     /**
40561      * @cfg {String} triggerClass
40562      * An additional CSS class used to style the trigger button.  The trigger will always get the
40563      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40564      * which displays a calendar icon).
40565      */
40566     triggerClass : 'x-form-date-trigger',
40567     
40568
40569     /**
40570      * @cfg {Boolean} useIso
40571      * if enabled, then the date field will use a hidden field to store the 
40572      * real value as iso formated date. default (true)
40573      */ 
40574     useIso : true,
40575     /**
40576      * @cfg {String/Object} autoCreate
40577      * A DomHelper element spec, or true for a default element spec (defaults to
40578      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40579      */ 
40580     // private
40581     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40582     
40583     // private
40584     hiddenField: false,
40585     
40586     hideMonthPicker : false,
40587     
40588     onRender : function(ct, position)
40589     {
40590         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40591         if (this.useIso) {
40592             this.el.dom.removeAttribute('name'); 
40593             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40594                     'before', true);
40595             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40596             // prevent input submission
40597             this.hiddenName = this.name;
40598         }
40599             
40600             
40601     },
40602     
40603     // private
40604     validateValue : function(value)
40605     {
40606         value = this.formatDate(value);
40607         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40608             return false;
40609         }
40610         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40611              return true;
40612         }
40613         var svalue = value;
40614         value = this.parseDate(value);
40615         if(!value){
40616             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40617             return false;
40618         }
40619         var time = value.getTime();
40620         if(this.minValue && time < this.minValue.getTime()){
40621             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40622             return false;
40623         }
40624         if(this.maxValue && time > this.maxValue.getTime()){
40625             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40626             return false;
40627         }
40628         /*if(this.disabledDays){
40629             var day = value.getDay();
40630             for(var i = 0; i < this.disabledDays.length; i++) {
40631                 if(day === this.disabledDays[i]){
40632                     this.markInvalid(this.disabledDaysText);
40633                     return false;
40634                 }
40635             }
40636         }
40637         */
40638         var fvalue = this.formatDate(value);
40639         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40640             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40641             return false;
40642         }
40643         */
40644         return true;
40645     },
40646
40647     // private
40648     // Provides logic to override the default TriggerField.validateBlur which just returns true
40649     validateBlur : function(){
40650         return !this.menu || !this.menu.isVisible();
40651     },
40652
40653     /**
40654      * Returns the current date value of the date field.
40655      * @return {Date} The date value
40656      */
40657     getValue : function(){
40658         
40659         
40660         
40661         return  this.hiddenField ?
40662                 this.hiddenField.value :
40663                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40664     },
40665
40666     /**
40667      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40668      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40669      * (the default format used is "m/d/y").
40670      * <br />Usage:
40671      * <pre><code>
40672 //All of these calls set the same date value (May 4, 2006)
40673
40674 //Pass a date object:
40675 var dt = new Date('5/4/06');
40676 monthField.setValue(dt);
40677
40678 //Pass a date string (default format):
40679 monthField.setValue('5/4/06');
40680
40681 //Pass a date string (custom format):
40682 monthField.format = 'Y-m-d';
40683 monthField.setValue('2006-5-4');
40684 </code></pre>
40685      * @param {String/Date} date The date or valid date string
40686      */
40687     setValue : function(date){
40688         Roo.log('month setValue' + date);
40689         // can only be first of month..
40690         
40691         var val = this.parseDate(date);
40692         
40693         if (this.hiddenField) {
40694             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40695         }
40696         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40697         this.value = this.parseDate(date);
40698     },
40699
40700     // private
40701     parseDate : function(value){
40702         if(!value || value instanceof Date){
40703             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40704             return value;
40705         }
40706         var v = Date.parseDate(value, this.format);
40707         if (!v && this.useIso) {
40708             v = Date.parseDate(value, 'Y-m-d');
40709         }
40710         if (v) {
40711             // 
40712             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40713         }
40714         
40715         
40716         if(!v && this.altFormats){
40717             if(!this.altFormatsArray){
40718                 this.altFormatsArray = this.altFormats.split("|");
40719             }
40720             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40721                 v = Date.parseDate(value, this.altFormatsArray[i]);
40722             }
40723         }
40724         return v;
40725     },
40726
40727     // private
40728     formatDate : function(date, fmt){
40729         return (!date || !(date instanceof Date)) ?
40730                date : date.dateFormat(fmt || this.format);
40731     },
40732
40733     // private
40734     menuListeners : {
40735         select: function(m, d){
40736             this.setValue(d);
40737             this.fireEvent('select', this, d);
40738         },
40739         show : function(){ // retain focus styling
40740             this.onFocus();
40741         },
40742         hide : function(){
40743             this.focus.defer(10, this);
40744             var ml = this.menuListeners;
40745             this.menu.un("select", ml.select,  this);
40746             this.menu.un("show", ml.show,  this);
40747             this.menu.un("hide", ml.hide,  this);
40748         }
40749     },
40750     // private
40751     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40752     onTriggerClick : function(){
40753         if(this.disabled){
40754             return;
40755         }
40756         if(this.menu == null){
40757             this.menu = new Roo.menu.DateMenu();
40758            
40759         }
40760         
40761         Roo.apply(this.menu.picker,  {
40762             
40763             showClear: this.allowBlank,
40764             minDate : this.minValue,
40765             maxDate : this.maxValue,
40766             disabledDatesRE : this.ddMatch,
40767             disabledDatesText : this.disabledDatesText,
40768             
40769             format : this.useIso ? 'Y-m-d' : this.format,
40770             minText : String.format(this.minText, this.formatDate(this.minValue)),
40771             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40772             
40773         });
40774          this.menu.on(Roo.apply({}, this.menuListeners, {
40775             scope:this
40776         }));
40777        
40778         
40779         var m = this.menu;
40780         var p = m.picker;
40781         
40782         // hide month picker get's called when we called by 'before hide';
40783         
40784         var ignorehide = true;
40785         p.hideMonthPicker  = function(disableAnim){
40786             if (ignorehide) {
40787                 return;
40788             }
40789              if(this.monthPicker){
40790                 Roo.log("hideMonthPicker called");
40791                 if(disableAnim === true){
40792                     this.monthPicker.hide();
40793                 }else{
40794                     this.monthPicker.slideOut('t', {duration:.2});
40795                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40796                     p.fireEvent("select", this, this.value);
40797                     m.hide();
40798                 }
40799             }
40800         }
40801         
40802         Roo.log('picker set value');
40803         Roo.log(this.getValue());
40804         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40805         m.show(this.el, 'tl-bl?');
40806         ignorehide  = false;
40807         // this will trigger hideMonthPicker..
40808         
40809         
40810         // hidden the day picker
40811         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40812         
40813         
40814         
40815       
40816         
40817         p.showMonthPicker.defer(100, p);
40818     
40819         
40820        
40821     },
40822
40823     beforeBlur : function(){
40824         var v = this.parseDate(this.getRawValue());
40825         if(v){
40826             this.setValue(v);
40827         }
40828     }
40829
40830     /** @cfg {Boolean} grow @hide */
40831     /** @cfg {Number} growMin @hide */
40832     /** @cfg {Number} growMax @hide */
40833     /**
40834      * @hide
40835      * @method autoSize
40836      */
40837 });/*
40838  * Based on:
40839  * Ext JS Library 1.1.1
40840  * Copyright(c) 2006-2007, Ext JS, LLC.
40841  *
40842  * Originally Released Under LGPL - original licence link has changed is not relivant.
40843  *
40844  * Fork - LGPL
40845  * <script type="text/javascript">
40846  */
40847  
40848
40849 /**
40850  * @class Roo.form.ComboBox
40851  * @extends Roo.form.TriggerField
40852  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40853  * @constructor
40854  * Create a new ComboBox.
40855  * @param {Object} config Configuration options
40856  */
40857 Roo.form.ComboBox = function(config){
40858     Roo.form.ComboBox.superclass.constructor.call(this, config);
40859     this.addEvents({
40860         /**
40861          * @event expand
40862          * Fires when the dropdown list is expanded
40863              * @param {Roo.form.ComboBox} combo This combo box
40864              */
40865         'expand' : true,
40866         /**
40867          * @event collapse
40868          * Fires when the dropdown list is collapsed
40869              * @param {Roo.form.ComboBox} combo This combo box
40870              */
40871         'collapse' : true,
40872         /**
40873          * @event beforeselect
40874          * Fires before a list item is selected. Return false to cancel the selection.
40875              * @param {Roo.form.ComboBox} combo This combo box
40876              * @param {Roo.data.Record} record The data record returned from the underlying store
40877              * @param {Number} index The index of the selected item in the dropdown list
40878              */
40879         'beforeselect' : true,
40880         /**
40881          * @event select
40882          * Fires when a list item is selected
40883              * @param {Roo.form.ComboBox} combo This combo box
40884              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40885              * @param {Number} index The index of the selected item in the dropdown list
40886              */
40887         'select' : true,
40888         /**
40889          * @event beforequery
40890          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
40891          * The event object passed has these properties:
40892              * @param {Roo.form.ComboBox} combo This combo box
40893              * @param {String} query The query
40894              * @param {Boolean} forceAll true to force "all" query
40895              * @param {Boolean} cancel true to cancel the query
40896              * @param {Object} e The query event object
40897              */
40898         'beforequery': true,
40899          /**
40900          * @event add
40901          * Fires when the 'add' icon is pressed (add a listener to enable add button)
40902              * @param {Roo.form.ComboBox} combo This combo box
40903              */
40904         'add' : true,
40905         /**
40906          * @event edit
40907          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
40908              * @param {Roo.form.ComboBox} combo This combo box
40909              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
40910              */
40911         'edit' : true
40912         
40913         
40914     });
40915     if(this.transform){
40916         this.allowDomMove = false;
40917         var s = Roo.getDom(this.transform);
40918         if(!this.hiddenName){
40919             this.hiddenName = s.name;
40920         }
40921         if(!this.store){
40922             this.mode = 'local';
40923             var d = [], opts = s.options;
40924             for(var i = 0, len = opts.length;i < len; i++){
40925                 var o = opts[i];
40926                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
40927                 if(o.selected) {
40928                     this.value = value;
40929                 }
40930                 d.push([value, o.text]);
40931             }
40932             this.store = new Roo.data.SimpleStore({
40933                 'id': 0,
40934                 fields: ['value', 'text'],
40935                 data : d
40936             });
40937             this.valueField = 'value';
40938             this.displayField = 'text';
40939         }
40940         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
40941         if(!this.lazyRender){
40942             this.target = true;
40943             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
40944             s.parentNode.removeChild(s); // remove it
40945             this.render(this.el.parentNode);
40946         }else{
40947             s.parentNode.removeChild(s); // remove it
40948         }
40949
40950     }
40951     if (this.store) {
40952         this.store = Roo.factory(this.store, Roo.data);
40953     }
40954     
40955     this.selectedIndex = -1;
40956     if(this.mode == 'local'){
40957         if(config.queryDelay === undefined){
40958             this.queryDelay = 10;
40959         }
40960         if(config.minChars === undefined){
40961             this.minChars = 0;
40962         }
40963     }
40964 };
40965
40966 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
40967     /**
40968      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
40969      */
40970     /**
40971      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
40972      * rendering into an Roo.Editor, defaults to false)
40973      */
40974     /**
40975      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
40976      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
40977      */
40978     /**
40979      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
40980      */
40981     /**
40982      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
40983      * the dropdown list (defaults to undefined, with no header element)
40984      */
40985
40986      /**
40987      * @cfg {String/Roo.Template} tpl The template to use to render the output
40988      */
40989      
40990     // private
40991     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
40992     /**
40993      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
40994      */
40995     listWidth: undefined,
40996     /**
40997      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
40998      * mode = 'remote' or 'text' if mode = 'local')
40999      */
41000     displayField: undefined,
41001     /**
41002      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41003      * mode = 'remote' or 'value' if mode = 'local'). 
41004      * Note: use of a valueField requires the user make a selection
41005      * in order for a value to be mapped.
41006      */
41007     valueField: undefined,
41008     
41009     
41010     /**
41011      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41012      * field's data value (defaults to the underlying DOM element's name)
41013      */
41014     hiddenName: undefined,
41015     /**
41016      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41017      */
41018     listClass: '',
41019     /**
41020      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41021      */
41022     selectedClass: 'x-combo-selected',
41023     /**
41024      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41025      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41026      * which displays a downward arrow icon).
41027      */
41028     triggerClass : 'x-form-arrow-trigger',
41029     /**
41030      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41031      */
41032     shadow:'sides',
41033     /**
41034      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41035      * anchor positions (defaults to 'tl-bl')
41036      */
41037     listAlign: 'tl-bl?',
41038     /**
41039      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41040      */
41041     maxHeight: 300,
41042     /**
41043      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41044      * query specified by the allQuery config option (defaults to 'query')
41045      */
41046     triggerAction: 'query',
41047     /**
41048      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41049      * (defaults to 4, does not apply if editable = false)
41050      */
41051     minChars : 4,
41052     /**
41053      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41054      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41055      */
41056     typeAhead: false,
41057     /**
41058      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41059      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41060      */
41061     queryDelay: 500,
41062     /**
41063      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41064      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41065      */
41066     pageSize: 0,
41067     /**
41068      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41069      * when editable = true (defaults to false)
41070      */
41071     selectOnFocus:false,
41072     /**
41073      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41074      */
41075     queryParam: 'query',
41076     /**
41077      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41078      * when mode = 'remote' (defaults to 'Loading...')
41079      */
41080     loadingText: 'Loading...',
41081     /**
41082      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41083      */
41084     resizable: false,
41085     /**
41086      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41087      */
41088     handleHeight : 8,
41089     /**
41090      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41091      * traditional select (defaults to true)
41092      */
41093     editable: true,
41094     /**
41095      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41096      */
41097     allQuery: '',
41098     /**
41099      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41100      */
41101     mode: 'remote',
41102     /**
41103      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41104      * listWidth has a higher value)
41105      */
41106     minListWidth : 70,
41107     /**
41108      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41109      * allow the user to set arbitrary text into the field (defaults to false)
41110      */
41111     forceSelection:false,
41112     /**
41113      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41114      * if typeAhead = true (defaults to 250)
41115      */
41116     typeAheadDelay : 250,
41117     /**
41118      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41119      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41120      */
41121     valueNotFoundText : undefined,
41122     /**
41123      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41124      */
41125     blockFocus : false,
41126     
41127     /**
41128      * @cfg {Boolean} disableClear Disable showing of clear button.
41129      */
41130     disableClear : false,
41131     /**
41132      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41133      */
41134     alwaysQuery : false,
41135     
41136     //private
41137     addicon : false,
41138     editicon: false,
41139     
41140     // element that contains real text value.. (when hidden is used..)
41141      
41142     // private
41143     onRender : function(ct, position){
41144         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41145         if(this.hiddenName){
41146             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41147                     'before', true);
41148             this.hiddenField.value =
41149                 this.hiddenValue !== undefined ? this.hiddenValue :
41150                 this.value !== undefined ? this.value : '';
41151
41152             // prevent input submission
41153             this.el.dom.removeAttribute('name');
41154              
41155              
41156         }
41157         if(Roo.isGecko){
41158             this.el.dom.setAttribute('autocomplete', 'off');
41159         }
41160
41161         var cls = 'x-combo-list';
41162
41163         this.list = new Roo.Layer({
41164             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41165         });
41166
41167         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41168         this.list.setWidth(lw);
41169         this.list.swallowEvent('mousewheel');
41170         this.assetHeight = 0;
41171
41172         if(this.title){
41173             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41174             this.assetHeight += this.header.getHeight();
41175         }
41176
41177         this.innerList = this.list.createChild({cls:cls+'-inner'});
41178         this.innerList.on('mouseover', this.onViewOver, this);
41179         this.innerList.on('mousemove', this.onViewMove, this);
41180         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41181         
41182         if(this.allowBlank && !this.pageSize && !this.disableClear){
41183             this.footer = this.list.createChild({cls:cls+'-ft'});
41184             this.pageTb = new Roo.Toolbar(this.footer);
41185            
41186         }
41187         if(this.pageSize){
41188             this.footer = this.list.createChild({cls:cls+'-ft'});
41189             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41190                     {pageSize: this.pageSize});
41191             
41192         }
41193         
41194         if (this.pageTb && this.allowBlank && !this.disableClear) {
41195             var _this = this;
41196             this.pageTb.add(new Roo.Toolbar.Fill(), {
41197                 cls: 'x-btn-icon x-btn-clear',
41198                 text: '&#160;',
41199                 handler: function()
41200                 {
41201                     _this.collapse();
41202                     _this.clearValue();
41203                     _this.onSelect(false, -1);
41204                 }
41205             });
41206         }
41207         if (this.footer) {
41208             this.assetHeight += this.footer.getHeight();
41209         }
41210         
41211
41212         if(!this.tpl){
41213             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41214         }
41215
41216         this.view = new Roo.View(this.innerList, this.tpl, {
41217             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41218         });
41219
41220         this.view.on('click', this.onViewClick, this);
41221
41222         this.store.on('beforeload', this.onBeforeLoad, this);
41223         this.store.on('load', this.onLoad, this);
41224         this.store.on('loadexception', this.onLoadException, this);
41225
41226         if(this.resizable){
41227             this.resizer = new Roo.Resizable(this.list,  {
41228                pinned:true, handles:'se'
41229             });
41230             this.resizer.on('resize', function(r, w, h){
41231                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41232                 this.listWidth = w;
41233                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41234                 this.restrictHeight();
41235             }, this);
41236             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41237         }
41238         if(!this.editable){
41239             this.editable = true;
41240             this.setEditable(false);
41241         }  
41242         
41243         
41244         if (typeof(this.events.add.listeners) != 'undefined') {
41245             
41246             this.addicon = this.wrap.createChild(
41247                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41248        
41249             this.addicon.on('click', function(e) {
41250                 this.fireEvent('add', this);
41251             }, this);
41252         }
41253         if (typeof(this.events.edit.listeners) != 'undefined') {
41254             
41255             this.editicon = this.wrap.createChild(
41256                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41257             if (this.addicon) {
41258                 this.editicon.setStyle('margin-left', '40px');
41259             }
41260             this.editicon.on('click', function(e) {
41261                 
41262                 // we fire even  if inothing is selected..
41263                 this.fireEvent('edit', this, this.lastData );
41264                 
41265             }, this);
41266         }
41267         
41268         
41269         
41270     },
41271
41272     // private
41273     initEvents : function(){
41274         Roo.form.ComboBox.superclass.initEvents.call(this);
41275
41276         this.keyNav = new Roo.KeyNav(this.el, {
41277             "up" : function(e){
41278                 this.inKeyMode = true;
41279                 this.selectPrev();
41280             },
41281
41282             "down" : function(e){
41283                 if(!this.isExpanded()){
41284                     this.onTriggerClick();
41285                 }else{
41286                     this.inKeyMode = true;
41287                     this.selectNext();
41288                 }
41289             },
41290
41291             "enter" : function(e){
41292                 this.onViewClick();
41293                 //return true;
41294             },
41295
41296             "esc" : function(e){
41297                 this.collapse();
41298             },
41299
41300             "tab" : function(e){
41301                 this.onViewClick(false);
41302                 this.fireEvent("specialkey", this, e);
41303                 return true;
41304             },
41305
41306             scope : this,
41307
41308             doRelay : function(foo, bar, hname){
41309                 if(hname == 'down' || this.scope.isExpanded()){
41310                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41311                 }
41312                 return true;
41313             },
41314
41315             forceKeyDown: true
41316         });
41317         this.queryDelay = Math.max(this.queryDelay || 10,
41318                 this.mode == 'local' ? 10 : 250);
41319         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41320         if(this.typeAhead){
41321             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41322         }
41323         if(this.editable !== false){
41324             this.el.on("keyup", this.onKeyUp, this);
41325         }
41326         if(this.forceSelection){
41327             this.on('blur', this.doForce, this);
41328         }
41329     },
41330
41331     onDestroy : function(){
41332         if(this.view){
41333             this.view.setStore(null);
41334             this.view.el.removeAllListeners();
41335             this.view.el.remove();
41336             this.view.purgeListeners();
41337         }
41338         if(this.list){
41339             this.list.destroy();
41340         }
41341         if(this.store){
41342             this.store.un('beforeload', this.onBeforeLoad, this);
41343             this.store.un('load', this.onLoad, this);
41344             this.store.un('loadexception', this.onLoadException, this);
41345         }
41346         Roo.form.ComboBox.superclass.onDestroy.call(this);
41347     },
41348
41349     // private
41350     fireKey : function(e){
41351         if(e.isNavKeyPress() && !this.list.isVisible()){
41352             this.fireEvent("specialkey", this, e);
41353         }
41354     },
41355
41356     // private
41357     onResize: function(w, h){
41358         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41359         
41360         if(typeof w != 'number'){
41361             // we do not handle it!?!?
41362             return;
41363         }
41364         var tw = this.trigger.getWidth();
41365         tw += this.addicon ? this.addicon.getWidth() : 0;
41366         tw += this.editicon ? this.editicon.getWidth() : 0;
41367         var x = w - tw;
41368         this.el.setWidth( this.adjustWidth('input', x));
41369             
41370         this.trigger.setStyle('left', x+'px');
41371         
41372         if(this.list && this.listWidth === undefined){
41373             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41374             this.list.setWidth(lw);
41375             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41376         }
41377         
41378     
41379         
41380     },
41381
41382     /**
41383      * Allow or prevent the user from directly editing the field text.  If false is passed,
41384      * the user will only be able to select from the items defined in the dropdown list.  This method
41385      * is the runtime equivalent of setting the 'editable' config option at config time.
41386      * @param {Boolean} value True to allow the user to directly edit the field text
41387      */
41388     setEditable : function(value){
41389         if(value == this.editable){
41390             return;
41391         }
41392         this.editable = value;
41393         if(!value){
41394             this.el.dom.setAttribute('readOnly', true);
41395             this.el.on('mousedown', this.onTriggerClick,  this);
41396             this.el.addClass('x-combo-noedit');
41397         }else{
41398             this.el.dom.setAttribute('readOnly', false);
41399             this.el.un('mousedown', this.onTriggerClick,  this);
41400             this.el.removeClass('x-combo-noedit');
41401         }
41402     },
41403
41404     // private
41405     onBeforeLoad : function(){
41406         if(!this.hasFocus){
41407             return;
41408         }
41409         this.innerList.update(this.loadingText ?
41410                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41411         this.restrictHeight();
41412         this.selectedIndex = -1;
41413     },
41414
41415     // private
41416     onLoad : function(){
41417         if(!this.hasFocus){
41418             return;
41419         }
41420         if(this.store.getCount() > 0){
41421             this.expand();
41422             this.restrictHeight();
41423             if(this.lastQuery == this.allQuery){
41424                 if(this.editable){
41425                     this.el.dom.select();
41426                 }
41427                 if(!this.selectByValue(this.value, true)){
41428                     this.select(0, true);
41429                 }
41430             }else{
41431                 this.selectNext();
41432                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41433                     this.taTask.delay(this.typeAheadDelay);
41434                 }
41435             }
41436         }else{
41437             this.onEmptyResults();
41438         }
41439         //this.el.focus();
41440     },
41441     // private
41442     onLoadException : function()
41443     {
41444         this.collapse();
41445         Roo.log(this.store.reader.jsonData);
41446         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41447             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41448         }
41449         
41450         
41451     },
41452     // private
41453     onTypeAhead : function(){
41454         if(this.store.getCount() > 0){
41455             var r = this.store.getAt(0);
41456             var newValue = r.data[this.displayField];
41457             var len = newValue.length;
41458             var selStart = this.getRawValue().length;
41459             if(selStart != len){
41460                 this.setRawValue(newValue);
41461                 this.selectText(selStart, newValue.length);
41462             }
41463         }
41464     },
41465
41466     // private
41467     onSelect : function(record, index){
41468         if(this.fireEvent('beforeselect', this, record, index) !== false){
41469             this.setFromData(index > -1 ? record.data : false);
41470             this.collapse();
41471             this.fireEvent('select', this, record, index);
41472         }
41473     },
41474
41475     /**
41476      * Returns the currently selected field value or empty string if no value is set.
41477      * @return {String} value The selected value
41478      */
41479     getValue : function(){
41480         if(this.valueField){
41481             return typeof this.value != 'undefined' ? this.value : '';
41482         }
41483         return Roo.form.ComboBox.superclass.getValue.call(this);
41484     },
41485
41486     /**
41487      * Clears any text/value currently set in the field
41488      */
41489     clearValue : function(){
41490         if(this.hiddenField){
41491             this.hiddenField.value = '';
41492         }
41493         this.value = '';
41494         this.setRawValue('');
41495         this.lastSelectionText = '';
41496         
41497     },
41498
41499     /**
41500      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41501      * will be displayed in the field.  If the value does not match the data value of an existing item,
41502      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41503      * Otherwise the field will be blank (although the value will still be set).
41504      * @param {String} value The value to match
41505      */
41506     setValue : function(v){
41507         var text = v;
41508         if(this.valueField){
41509             var r = this.findRecord(this.valueField, v);
41510             if(r){
41511                 text = r.data[this.displayField];
41512             }else if(this.valueNotFoundText !== undefined){
41513                 text = this.valueNotFoundText;
41514             }
41515         }
41516         this.lastSelectionText = text;
41517         if(this.hiddenField){
41518             this.hiddenField.value = v;
41519         }
41520         Roo.form.ComboBox.superclass.setValue.call(this, text);
41521         this.value = v;
41522     },
41523     /**
41524      * @property {Object} the last set data for the element
41525      */
41526     
41527     lastData : false,
41528     /**
41529      * Sets the value of the field based on a object which is related to the record format for the store.
41530      * @param {Object} value the value to set as. or false on reset?
41531      */
41532     setFromData : function(o){
41533         var dv = ''; // display value
41534         var vv = ''; // value value..
41535         this.lastData = o;
41536         if (this.displayField) {
41537             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41538         } else {
41539             // this is an error condition!!!
41540             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41541         }
41542         
41543         if(this.valueField){
41544             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41545         }
41546         if(this.hiddenField){
41547             this.hiddenField.value = vv;
41548             
41549             this.lastSelectionText = dv;
41550             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41551             this.value = vv;
41552             return;
41553         }
41554         // no hidden field.. - we store the value in 'value', but still display
41555         // display field!!!!
41556         this.lastSelectionText = dv;
41557         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41558         this.value = vv;
41559         
41560         
41561     },
41562     // private
41563     reset : function(){
41564         // overridden so that last data is reset..
41565         this.setValue(this.resetValue);
41566         this.clearInvalid();
41567         this.lastData = false;
41568         if (this.view) {
41569             this.view.clearSelections();
41570         }
41571     },
41572     // private
41573     findRecord : function(prop, value){
41574         var record;
41575         if(this.store.getCount() > 0){
41576             this.store.each(function(r){
41577                 if(r.data[prop] == value){
41578                     record = r;
41579                     return false;
41580                 }
41581                 return true;
41582             });
41583         }
41584         return record;
41585     },
41586     
41587     getName: function()
41588     {
41589         // returns hidden if it's set..
41590         if (!this.rendered) {return ''};
41591         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41592         
41593     },
41594     // private
41595     onViewMove : function(e, t){
41596         this.inKeyMode = false;
41597     },
41598
41599     // private
41600     onViewOver : function(e, t){
41601         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41602             return;
41603         }
41604         var item = this.view.findItemFromChild(t);
41605         if(item){
41606             var index = this.view.indexOf(item);
41607             this.select(index, false);
41608         }
41609     },
41610
41611     // private
41612     onViewClick : function(doFocus)
41613     {
41614         var index = this.view.getSelectedIndexes()[0];
41615         var r = this.store.getAt(index);
41616         if(r){
41617             this.onSelect(r, index);
41618         }
41619         if(doFocus !== false && !this.blockFocus){
41620             this.el.focus();
41621         }
41622     },
41623
41624     // private
41625     restrictHeight : function(){
41626         this.innerList.dom.style.height = '';
41627         var inner = this.innerList.dom;
41628         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41629         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41630         this.list.beginUpdate();
41631         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41632         this.list.alignTo(this.el, this.listAlign);
41633         this.list.endUpdate();
41634     },
41635
41636     // private
41637     onEmptyResults : function(){
41638         this.collapse();
41639     },
41640
41641     /**
41642      * Returns true if the dropdown list is expanded, else false.
41643      */
41644     isExpanded : function(){
41645         return this.list.isVisible();
41646     },
41647
41648     /**
41649      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41650      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41651      * @param {String} value The data value of the item to select
41652      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41653      * selected item if it is not currently in view (defaults to true)
41654      * @return {Boolean} True if the value matched an item in the list, else false
41655      */
41656     selectByValue : function(v, scrollIntoView){
41657         if(v !== undefined && v !== null){
41658             var r = this.findRecord(this.valueField || this.displayField, v);
41659             if(r){
41660                 this.select(this.store.indexOf(r), scrollIntoView);
41661                 return true;
41662             }
41663         }
41664         return false;
41665     },
41666
41667     /**
41668      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41669      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41670      * @param {Number} index The zero-based index of the list item to select
41671      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41672      * selected item if it is not currently in view (defaults to true)
41673      */
41674     select : function(index, scrollIntoView){
41675         this.selectedIndex = index;
41676         this.view.select(index);
41677         if(scrollIntoView !== false){
41678             var el = this.view.getNode(index);
41679             if(el){
41680                 this.innerList.scrollChildIntoView(el, false);
41681             }
41682         }
41683     },
41684
41685     // private
41686     selectNext : function(){
41687         var ct = this.store.getCount();
41688         if(ct > 0){
41689             if(this.selectedIndex == -1){
41690                 this.select(0);
41691             }else if(this.selectedIndex < ct-1){
41692                 this.select(this.selectedIndex+1);
41693             }
41694         }
41695     },
41696
41697     // private
41698     selectPrev : function(){
41699         var ct = this.store.getCount();
41700         if(ct > 0){
41701             if(this.selectedIndex == -1){
41702                 this.select(0);
41703             }else if(this.selectedIndex != 0){
41704                 this.select(this.selectedIndex-1);
41705             }
41706         }
41707     },
41708
41709     // private
41710     onKeyUp : function(e){
41711         if(this.editable !== false && !e.isSpecialKey()){
41712             this.lastKey = e.getKey();
41713             this.dqTask.delay(this.queryDelay);
41714         }
41715     },
41716
41717     // private
41718     validateBlur : function(){
41719         return !this.list || !this.list.isVisible();   
41720     },
41721
41722     // private
41723     initQuery : function(){
41724         this.doQuery(this.getRawValue());
41725     },
41726
41727     // private
41728     doForce : function(){
41729         if(this.el.dom.value.length > 0){
41730             this.el.dom.value =
41731                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41732              
41733         }
41734     },
41735
41736     /**
41737      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41738      * query allowing the query action to be canceled if needed.
41739      * @param {String} query The SQL query to execute
41740      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41741      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41742      * saved in the current store (defaults to false)
41743      */
41744     doQuery : function(q, forceAll){
41745         if(q === undefined || q === null){
41746             q = '';
41747         }
41748         var qe = {
41749             query: q,
41750             forceAll: forceAll,
41751             combo: this,
41752             cancel:false
41753         };
41754         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41755             return false;
41756         }
41757         q = qe.query;
41758         forceAll = qe.forceAll;
41759         if(forceAll === true || (q.length >= this.minChars)){
41760             if(this.lastQuery != q || this.alwaysQuery){
41761                 this.lastQuery = q;
41762                 if(this.mode == 'local'){
41763                     this.selectedIndex = -1;
41764                     if(forceAll){
41765                         this.store.clearFilter();
41766                     }else{
41767                         this.store.filter(this.displayField, q);
41768                     }
41769                     this.onLoad();
41770                 }else{
41771                     this.store.baseParams[this.queryParam] = q;
41772                     this.store.load({
41773                         params: this.getParams(q)
41774                     });
41775                     this.expand();
41776                 }
41777             }else{
41778                 this.selectedIndex = -1;
41779                 this.onLoad();   
41780             }
41781         }
41782     },
41783
41784     // private
41785     getParams : function(q){
41786         var p = {};
41787         //p[this.queryParam] = q;
41788         if(this.pageSize){
41789             p.start = 0;
41790             p.limit = this.pageSize;
41791         }
41792         return p;
41793     },
41794
41795     /**
41796      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41797      */
41798     collapse : function(){
41799         if(!this.isExpanded()){
41800             return;
41801         }
41802         this.list.hide();
41803         Roo.get(document).un('mousedown', this.collapseIf, this);
41804         Roo.get(document).un('mousewheel', this.collapseIf, this);
41805         if (!this.editable) {
41806             Roo.get(document).un('keydown', this.listKeyPress, this);
41807         }
41808         this.fireEvent('collapse', this);
41809     },
41810
41811     // private
41812     collapseIf : function(e){
41813         if(!e.within(this.wrap) && !e.within(this.list)){
41814             this.collapse();
41815         }
41816     },
41817
41818     /**
41819      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41820      */
41821     expand : function(){
41822         if(this.isExpanded() || !this.hasFocus){
41823             return;
41824         }
41825         this.list.alignTo(this.el, this.listAlign);
41826         this.list.show();
41827         Roo.get(document).on('mousedown', this.collapseIf, this);
41828         Roo.get(document).on('mousewheel', this.collapseIf, this);
41829         if (!this.editable) {
41830             Roo.get(document).on('keydown', this.listKeyPress, this);
41831         }
41832         
41833         this.fireEvent('expand', this);
41834     },
41835
41836     // private
41837     // Implements the default empty TriggerField.onTriggerClick function
41838     onTriggerClick : function(){
41839         if(this.disabled){
41840             return;
41841         }
41842         if(this.isExpanded()){
41843             this.collapse();
41844             if (!this.blockFocus) {
41845                 this.el.focus();
41846             }
41847             
41848         }else {
41849             this.hasFocus = true;
41850             if(this.triggerAction == 'all') {
41851                 this.doQuery(this.allQuery, true);
41852             } else {
41853                 this.doQuery(this.getRawValue());
41854             }
41855             if (!this.blockFocus) {
41856                 this.el.focus();
41857             }
41858         }
41859     },
41860     listKeyPress : function(e)
41861     {
41862         //Roo.log('listkeypress');
41863         // scroll to first matching element based on key pres..
41864         if (e.isSpecialKey()) {
41865             return false;
41866         }
41867         var k = String.fromCharCode(e.getKey()).toUpperCase();
41868         //Roo.log(k);
41869         var match  = false;
41870         var csel = this.view.getSelectedNodes();
41871         var cselitem = false;
41872         if (csel.length) {
41873             var ix = this.view.indexOf(csel[0]);
41874             cselitem  = this.store.getAt(ix);
41875             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41876                 cselitem = false;
41877             }
41878             
41879         }
41880         
41881         this.store.each(function(v) { 
41882             if (cselitem) {
41883                 // start at existing selection.
41884                 if (cselitem.id == v.id) {
41885                     cselitem = false;
41886                 }
41887                 return;
41888             }
41889                 
41890             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
41891                 match = this.store.indexOf(v);
41892                 return false;
41893             }
41894         }, this);
41895         
41896         if (match === false) {
41897             return true; // no more action?
41898         }
41899         // scroll to?
41900         this.view.select(match);
41901         var sn = Roo.get(this.view.getSelectedNodes()[0]);
41902         sn.scrollIntoView(sn.dom.parentNode, false);
41903     }
41904
41905     /** 
41906     * @cfg {Boolean} grow 
41907     * @hide 
41908     */
41909     /** 
41910     * @cfg {Number} growMin 
41911     * @hide 
41912     */
41913     /** 
41914     * @cfg {Number} growMax 
41915     * @hide 
41916     */
41917     /**
41918      * @hide
41919      * @method autoSize
41920      */
41921 });/*
41922  * Copyright(c) 2010-2012, Roo J Solutions Limited
41923  *
41924  * Licence LGPL
41925  *
41926  */
41927
41928 /**
41929  * @class Roo.form.ComboBoxArray
41930  * @extends Roo.form.TextField
41931  * A facebook style adder... for lists of email / people / countries  etc...
41932  * pick multiple items from a combo box, and shows each one.
41933  *
41934  *  Fred [x]  Brian [x]  [Pick another |v]
41935  *
41936  *
41937  *  For this to work: it needs various extra information
41938  *    - normal combo problay has
41939  *      name, hiddenName
41940  *    + displayField, valueField
41941  *
41942  *    For our purpose...
41943  *
41944  *
41945  *   If we change from 'extends' to wrapping...
41946  *   
41947  *  
41948  *
41949  
41950  
41951  * @constructor
41952  * Create a new ComboBoxArray.
41953  * @param {Object} config Configuration options
41954  */
41955  
41956
41957 Roo.form.ComboBoxArray = function(config)
41958 {
41959     this.addEvents({
41960         /**
41961          * @event beforeremove
41962          * Fires before remove the value from the list
41963              * @param {Roo.form.ComboBoxArray} _self This combo box array
41964              * @param {Roo.form.ComboBoxArray.Item} item removed item
41965              */
41966         'beforeremove' : true,
41967         /**
41968          * @event remove
41969          * Fires when remove the value from the list
41970              * @param {Roo.form.ComboBoxArray} _self This combo box array
41971              * @param {Roo.form.ComboBoxArray.Item} item removed item
41972              */
41973         'remove' : true
41974         
41975         
41976     });
41977     
41978     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
41979     
41980     this.items = new Roo.util.MixedCollection(false);
41981     
41982     // construct the child combo...
41983     
41984     
41985     
41986     
41987    
41988     
41989 }
41990
41991  
41992 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
41993
41994     /**
41995      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
41996      */
41997     
41998     lastData : false,
41999     
42000     // behavies liek a hiddne field
42001     inputType:      'hidden',
42002     /**
42003      * @cfg {Number} width The width of the box that displays the selected element
42004      */ 
42005     width:          300,
42006
42007     
42008     
42009     /**
42010      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42011      */
42012     name : false,
42013     /**
42014      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42015      */
42016     hiddenName : false,
42017     
42018     
42019     // private the array of items that are displayed..
42020     items  : false,
42021     // private - the hidden field el.
42022     hiddenEl : false,
42023     // private - the filed el..
42024     el : false,
42025     
42026     //validateValue : function() { return true; }, // all values are ok!
42027     //onAddClick: function() { },
42028     
42029     onRender : function(ct, position) 
42030     {
42031         
42032         // create the standard hidden element
42033         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42034         
42035         
42036         // give fake names to child combo;
42037         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42038         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
42039         
42040         this.combo = Roo.factory(this.combo, Roo.form);
42041         this.combo.onRender(ct, position);
42042         if (typeof(this.combo.width) != 'undefined') {
42043             this.combo.onResize(this.combo.width,0);
42044         }
42045         
42046         this.combo.initEvents();
42047         
42048         // assigned so form know we need to do this..
42049         this.store          = this.combo.store;
42050         this.valueField     = this.combo.valueField;
42051         this.displayField   = this.combo.displayField ;
42052         
42053         
42054         this.combo.wrap.addClass('x-cbarray-grp');
42055         
42056         var cbwrap = this.combo.wrap.createChild(
42057             {tag: 'div', cls: 'x-cbarray-cb'},
42058             this.combo.el.dom
42059         );
42060         
42061              
42062         this.hiddenEl = this.combo.wrap.createChild({
42063             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42064         });
42065         this.el = this.combo.wrap.createChild({
42066             tag: 'input',  type:'hidden' , name: this.name, value : ''
42067         });
42068          //   this.el.dom.removeAttribute("name");
42069         
42070         
42071         this.outerWrap = this.combo.wrap;
42072         this.wrap = cbwrap;
42073         
42074         this.outerWrap.setWidth(this.width);
42075         this.outerWrap.dom.removeChild(this.el.dom);
42076         
42077         this.wrap.dom.appendChild(this.el.dom);
42078         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42079         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42080         
42081         this.combo.trigger.setStyle('position','relative');
42082         this.combo.trigger.setStyle('left', '0px');
42083         this.combo.trigger.setStyle('top', '2px');
42084         
42085         this.combo.el.setStyle('vertical-align', 'text-bottom');
42086         
42087         //this.trigger.setStyle('vertical-align', 'top');
42088         
42089         // this should use the code from combo really... on('add' ....)
42090         if (this.adder) {
42091             
42092         
42093             this.adder = this.outerWrap.createChild(
42094                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42095             var _t = this;
42096             this.adder.on('click', function(e) {
42097                 _t.fireEvent('adderclick', this, e);
42098             }, _t);
42099         }
42100         //var _t = this;
42101         //this.adder.on('click', this.onAddClick, _t);
42102         
42103         
42104         this.combo.on('select', function(cb, rec, ix) {
42105             this.addItem(rec.data);
42106             
42107             cb.setValue('');
42108             cb.el.dom.value = '';
42109             //cb.lastData = rec.data;
42110             // add to list
42111             
42112         }, this);
42113         
42114         
42115     },
42116     
42117     
42118     getName: function()
42119     {
42120         // returns hidden if it's set..
42121         if (!this.rendered) {return ''};
42122         return  this.hiddenName ? this.hiddenName : this.name;
42123         
42124     },
42125     
42126     
42127     onResize: function(w, h){
42128         
42129         return;
42130         // not sure if this is needed..
42131         //this.combo.onResize(w,h);
42132         
42133         if(typeof w != 'number'){
42134             // we do not handle it!?!?
42135             return;
42136         }
42137         var tw = this.combo.trigger.getWidth();
42138         tw += this.addicon ? this.addicon.getWidth() : 0;
42139         tw += this.editicon ? this.editicon.getWidth() : 0;
42140         var x = w - tw;
42141         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42142             
42143         this.combo.trigger.setStyle('left', '0px');
42144         
42145         if(this.list && this.listWidth === undefined){
42146             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42147             this.list.setWidth(lw);
42148             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42149         }
42150         
42151     
42152         
42153     },
42154     
42155     addItem: function(rec)
42156     {
42157         var valueField = this.combo.valueField;
42158         var displayField = this.combo.displayField;
42159         if (this.items.indexOfKey(rec[valueField]) > -1) {
42160             //console.log("GOT " + rec.data.id);
42161             return;
42162         }
42163         
42164         var x = new Roo.form.ComboBoxArray.Item({
42165             //id : rec[this.idField],
42166             data : rec,
42167             displayField : displayField ,
42168             tipField : displayField ,
42169             cb : this
42170         });
42171         // use the 
42172         this.items.add(rec[valueField],x);
42173         // add it before the element..
42174         this.updateHiddenEl();
42175         x.render(this.outerWrap, this.wrap.dom);
42176         // add the image handler..
42177     },
42178     
42179     updateHiddenEl : function()
42180     {
42181         this.validate();
42182         if (!this.hiddenEl) {
42183             return;
42184         }
42185         var ar = [];
42186         var idField = this.combo.valueField;
42187         
42188         this.items.each(function(f) {
42189             ar.push(f.data[idField]);
42190            
42191         });
42192         this.hiddenEl.dom.value = ar.join(',');
42193         this.validate();
42194     },
42195     
42196     reset : function()
42197     {
42198         this.items.clear();
42199         
42200         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42201            el.remove();
42202         });
42203         
42204         this.el.dom.value = '';
42205         if (this.hiddenEl) {
42206             this.hiddenEl.dom.value = '';
42207         }
42208         
42209     },
42210     getValue: function()
42211     {
42212         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42213     },
42214     setValue: function(v) // not a valid action - must use addItems..
42215     {
42216          
42217         this.reset();
42218         
42219         
42220         
42221         if (this.store.isLocal && (typeof(v) == 'string')) {
42222             // then we can use the store to find the values..
42223             // comma seperated at present.. this needs to allow JSON based encoding..
42224             this.hiddenEl.value  = v;
42225             var v_ar = [];
42226             Roo.each(v.split(','), function(k) {
42227                 Roo.log("CHECK " + this.valueField + ',' + k);
42228                 var li = this.store.query(this.valueField, k);
42229                 if (!li.length) {
42230                     return;
42231                 }
42232                 var add = {};
42233                 add[this.valueField] = k;
42234                 add[this.displayField] = li.item(0).data[this.displayField];
42235                 
42236                 this.addItem(add);
42237             }, this) 
42238              
42239         }
42240         if (typeof(v) == 'object' ) {
42241             // then let's assume it's an array of objects..
42242             Roo.each(v, function(l) {
42243                 this.addItem(l);
42244             }, this);
42245              
42246         }
42247         
42248         
42249     },
42250     setFromData: function(v)
42251     {
42252         // this recieves an object, if setValues is called.
42253         this.reset();
42254         this.el.dom.value = v[this.displayField];
42255         this.hiddenEl.dom.value = v[this.valueField];
42256         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42257             return;
42258         }
42259         var kv = v[this.valueField];
42260         var dv = v[this.displayField];
42261         kv = typeof(kv) != 'string' ? '' : kv;
42262         dv = typeof(dv) != 'string' ? '' : dv;
42263         
42264         
42265         var keys = kv.split(',');
42266         var display = dv.split(',');
42267         for (var i = 0 ; i < keys.length; i++) {
42268             
42269             add = {};
42270             add[this.valueField] = keys[i];
42271             add[this.displayField] = display[i];
42272             this.addItem(add);
42273         }
42274       
42275         
42276     },
42277     
42278     /**
42279      * Validates the combox array value
42280      * @return {Boolean} True if the value is valid, else false
42281      */
42282     validate : function(){
42283         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42284             this.clearInvalid();
42285             return true;
42286         }
42287         return false;
42288     },
42289     
42290     validateValue : function(value){
42291         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42292         
42293     },
42294     
42295     /*@
42296      * overide
42297      * 
42298      */
42299     isDirty : function() {
42300         if(this.disabled) {
42301             return false;
42302         }
42303         
42304         try {
42305             var d = Roo.decode(String(this.originalValue));
42306         } catch (e) {
42307             return String(this.getValue()) !== String(this.originalValue);
42308         }
42309         
42310         var originalValue = [];
42311         
42312         for (var i = 0; i < d.length; i++){
42313             originalValue.push(d[i][this.valueField]);
42314         }
42315         
42316         return String(this.getValue()) !== String(originalValue.join(','));
42317         
42318     }
42319     
42320 });
42321
42322
42323
42324 /**
42325  * @class Roo.form.ComboBoxArray.Item
42326  * @extends Roo.BoxComponent
42327  * A selected item in the list
42328  *  Fred [x]  Brian [x]  [Pick another |v]
42329  * 
42330  * @constructor
42331  * Create a new item.
42332  * @param {Object} config Configuration options
42333  */
42334  
42335 Roo.form.ComboBoxArray.Item = function(config) {
42336     config.id = Roo.id();
42337     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42338 }
42339
42340 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42341     data : {},
42342     cb: false,
42343     displayField : false,
42344     tipField : false,
42345     
42346     
42347     defaultAutoCreate : {
42348         tag: 'div',
42349         cls: 'x-cbarray-item',
42350         cn : [ 
42351             { tag: 'div' },
42352             {
42353                 tag: 'img',
42354                 width:16,
42355                 height : 16,
42356                 src : Roo.BLANK_IMAGE_URL ,
42357                 align: 'center'
42358             }
42359         ]
42360         
42361     },
42362     
42363  
42364     onRender : function(ct, position)
42365     {
42366         Roo.form.Field.superclass.onRender.call(this, ct, position);
42367         
42368         if(!this.el){
42369             var cfg = this.getAutoCreate();
42370             this.el = ct.createChild(cfg, position);
42371         }
42372         
42373         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42374         
42375         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42376             this.cb.renderer(this.data) :
42377             String.format('{0}',this.data[this.displayField]);
42378         
42379             
42380         this.el.child('div').dom.setAttribute('qtip',
42381                         String.format('{0}',this.data[this.tipField])
42382         );
42383         
42384         this.el.child('img').on('click', this.remove, this);
42385         
42386     },
42387    
42388     remove : function()
42389     {
42390         if(this.cb.disabled){
42391             return;
42392         }
42393         
42394         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42395             this.cb.items.remove(this);
42396             this.el.child('img').un('click', this.remove, this);
42397             this.el.remove();
42398             this.cb.updateHiddenEl();
42399
42400             this.cb.fireEvent('remove', this.cb, this);
42401         }
42402         
42403     }
42404 });/*
42405  * Based on:
42406  * Ext JS Library 1.1.1
42407  * Copyright(c) 2006-2007, Ext JS, LLC.
42408  *
42409  * Originally Released Under LGPL - original licence link has changed is not relivant.
42410  *
42411  * Fork - LGPL
42412  * <script type="text/javascript">
42413  */
42414 /**
42415  * @class Roo.form.Checkbox
42416  * @extends Roo.form.Field
42417  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42418  * @constructor
42419  * Creates a new Checkbox
42420  * @param {Object} config Configuration options
42421  */
42422 Roo.form.Checkbox = function(config){
42423     Roo.form.Checkbox.superclass.constructor.call(this, config);
42424     this.addEvents({
42425         /**
42426          * @event check
42427          * Fires when the checkbox is checked or unchecked.
42428              * @param {Roo.form.Checkbox} this This checkbox
42429              * @param {Boolean} checked The new checked value
42430              */
42431         check : true
42432     });
42433 };
42434
42435 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42436     /**
42437      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42438      */
42439     focusClass : undefined,
42440     /**
42441      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42442      */
42443     fieldClass: "x-form-field",
42444     /**
42445      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42446      */
42447     checked: false,
42448     /**
42449      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42450      * {tag: "input", type: "checkbox", autocomplete: "off"})
42451      */
42452     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42453     /**
42454      * @cfg {String} boxLabel The text that appears beside the checkbox
42455      */
42456     boxLabel : "",
42457     /**
42458      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42459      */  
42460     inputValue : '1',
42461     /**
42462      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42463      */
42464      valueOff: '0', // value when not checked..
42465
42466     actionMode : 'viewEl', 
42467     //
42468     // private
42469     itemCls : 'x-menu-check-item x-form-item',
42470     groupClass : 'x-menu-group-item',
42471     inputType : 'hidden',
42472     
42473     
42474     inSetChecked: false, // check that we are not calling self...
42475     
42476     inputElement: false, // real input element?
42477     basedOn: false, // ????
42478     
42479     isFormField: true, // not sure where this is needed!!!!
42480
42481     onResize : function(){
42482         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42483         if(!this.boxLabel){
42484             this.el.alignTo(this.wrap, 'c-c');
42485         }
42486     },
42487
42488     initEvents : function(){
42489         Roo.form.Checkbox.superclass.initEvents.call(this);
42490         this.el.on("click", this.onClick,  this);
42491         this.el.on("change", this.onClick,  this);
42492     },
42493
42494
42495     getResizeEl : function(){
42496         return this.wrap;
42497     },
42498
42499     getPositionEl : function(){
42500         return this.wrap;
42501     },
42502
42503     // private
42504     onRender : function(ct, position){
42505         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42506         /*
42507         if(this.inputValue !== undefined){
42508             this.el.dom.value = this.inputValue;
42509         }
42510         */
42511         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42512         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42513         var viewEl = this.wrap.createChild({ 
42514             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42515         this.viewEl = viewEl;   
42516         this.wrap.on('click', this.onClick,  this); 
42517         
42518         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42519         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42520         
42521         
42522         
42523         if(this.boxLabel){
42524             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42525         //    viewEl.on('click', this.onClick,  this); 
42526         }
42527         //if(this.checked){
42528             this.setChecked(this.checked);
42529         //}else{
42530             //this.checked = this.el.dom;
42531         //}
42532
42533     },
42534
42535     // private
42536     initValue : Roo.emptyFn,
42537
42538     /**
42539      * Returns the checked state of the checkbox.
42540      * @return {Boolean} True if checked, else false
42541      */
42542     getValue : function(){
42543         if(this.el){
42544             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42545         }
42546         return this.valueOff;
42547         
42548     },
42549
42550         // private
42551     onClick : function(){ 
42552         if (this.disabled) {
42553             return;
42554         }
42555         this.setChecked(!this.checked);
42556
42557         //if(this.el.dom.checked != this.checked){
42558         //    this.setValue(this.el.dom.checked);
42559        // }
42560     },
42561
42562     /**
42563      * Sets the checked state of the checkbox.
42564      * On is always based on a string comparison between inputValue and the param.
42565      * @param {Boolean/String} value - the value to set 
42566      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42567      */
42568     setValue : function(v,suppressEvent){
42569         
42570         
42571         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42572         //if(this.el && this.el.dom){
42573         //    this.el.dom.checked = this.checked;
42574         //    this.el.dom.defaultChecked = this.checked;
42575         //}
42576         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42577         //this.fireEvent("check", this, this.checked);
42578     },
42579     // private..
42580     setChecked : function(state,suppressEvent)
42581     {
42582         if (this.inSetChecked) {
42583             this.checked = state;
42584             return;
42585         }
42586         
42587     
42588         if(this.wrap){
42589             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42590         }
42591         this.checked = state;
42592         if(suppressEvent !== true){
42593             this.fireEvent('check', this, state);
42594         }
42595         this.inSetChecked = true;
42596         this.el.dom.value = state ? this.inputValue : this.valueOff;
42597         this.inSetChecked = false;
42598         
42599     },
42600     // handle setting of hidden value by some other method!!?!?
42601     setFromHidden: function()
42602     {
42603         if(!this.el){
42604             return;
42605         }
42606         //console.log("SET FROM HIDDEN");
42607         //alert('setFrom hidden');
42608         this.setValue(this.el.dom.value);
42609     },
42610     
42611     onDestroy : function()
42612     {
42613         if(this.viewEl){
42614             Roo.get(this.viewEl).remove();
42615         }
42616          
42617         Roo.form.Checkbox.superclass.onDestroy.call(this);
42618     },
42619     
42620     setBoxLabel : function(str)
42621     {
42622         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42623     }
42624
42625 });/*
42626  * Based on:
42627  * Ext JS Library 1.1.1
42628  * Copyright(c) 2006-2007, Ext JS, LLC.
42629  *
42630  * Originally Released Under LGPL - original licence link has changed is not relivant.
42631  *
42632  * Fork - LGPL
42633  * <script type="text/javascript">
42634  */
42635  
42636 /**
42637  * @class Roo.form.Radio
42638  * @extends Roo.form.Checkbox
42639  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42640  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42641  * @constructor
42642  * Creates a new Radio
42643  * @param {Object} config Configuration options
42644  */
42645 Roo.form.Radio = function(){
42646     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42647 };
42648 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42649     inputType: 'radio',
42650
42651     /**
42652      * If this radio is part of a group, it will return the selected value
42653      * @return {String}
42654      */
42655     getGroupValue : function(){
42656         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42657     },
42658     
42659     
42660     onRender : function(ct, position){
42661         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42662         
42663         if(this.inputValue !== undefined){
42664             this.el.dom.value = this.inputValue;
42665         }
42666          
42667         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42668         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42669         //var viewEl = this.wrap.createChild({ 
42670         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42671         //this.viewEl = viewEl;   
42672         //this.wrap.on('click', this.onClick,  this); 
42673         
42674         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42675         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42676         
42677         
42678         
42679         if(this.boxLabel){
42680             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42681         //    viewEl.on('click', this.onClick,  this); 
42682         }
42683          if(this.checked){
42684             this.el.dom.checked =   'checked' ;
42685         }
42686          
42687     } 
42688     
42689     
42690 });//<script type="text/javascript">
42691
42692 /*
42693  * Based  Ext JS Library 1.1.1
42694  * Copyright(c) 2006-2007, Ext JS, LLC.
42695  * LGPL
42696  *
42697  */
42698  
42699 /**
42700  * @class Roo.HtmlEditorCore
42701  * @extends Roo.Component
42702  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42703  *
42704  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42705  */
42706
42707 Roo.HtmlEditorCore = function(config){
42708     
42709     
42710     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42711     
42712     
42713     this.addEvents({
42714         /**
42715          * @event initialize
42716          * Fires when the editor is fully initialized (including the iframe)
42717          * @param {Roo.HtmlEditorCore} this
42718          */
42719         initialize: true,
42720         /**
42721          * @event activate
42722          * Fires when the editor is first receives the focus. Any insertion must wait
42723          * until after this event.
42724          * @param {Roo.HtmlEditorCore} this
42725          */
42726         activate: true,
42727          /**
42728          * @event beforesync
42729          * Fires before the textarea is updated with content from the editor iframe. Return false
42730          * to cancel the sync.
42731          * @param {Roo.HtmlEditorCore} this
42732          * @param {String} html
42733          */
42734         beforesync: true,
42735          /**
42736          * @event beforepush
42737          * Fires before the iframe editor is updated with content from the textarea. Return false
42738          * to cancel the push.
42739          * @param {Roo.HtmlEditorCore} this
42740          * @param {String} html
42741          */
42742         beforepush: true,
42743          /**
42744          * @event sync
42745          * Fires when the textarea is updated with content from the editor iframe.
42746          * @param {Roo.HtmlEditorCore} this
42747          * @param {String} html
42748          */
42749         sync: true,
42750          /**
42751          * @event push
42752          * Fires when the iframe editor is updated with content from the textarea.
42753          * @param {Roo.HtmlEditorCore} this
42754          * @param {String} html
42755          */
42756         push: true,
42757         
42758         /**
42759          * @event editorevent
42760          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42761          * @param {Roo.HtmlEditorCore} this
42762          */
42763         editorevent: true
42764         
42765     });
42766     
42767     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42768     
42769     // defaults : white / black...
42770     this.applyBlacklists();
42771     
42772     
42773     
42774 };
42775
42776
42777 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42778
42779
42780      /**
42781      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42782      */
42783     
42784     owner : false,
42785     
42786      /**
42787      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42788      *                        Roo.resizable.
42789      */
42790     resizable : false,
42791      /**
42792      * @cfg {Number} height (in pixels)
42793      */   
42794     height: 300,
42795    /**
42796      * @cfg {Number} width (in pixels)
42797      */   
42798     width: 500,
42799     
42800     /**
42801      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42802      * 
42803      */
42804     stylesheets: false,
42805     
42806     // id of frame..
42807     frameId: false,
42808     
42809     // private properties
42810     validationEvent : false,
42811     deferHeight: true,
42812     initialized : false,
42813     activated : false,
42814     sourceEditMode : false,
42815     onFocus : Roo.emptyFn,
42816     iframePad:3,
42817     hideMode:'offsets',
42818     
42819     clearUp: true,
42820     
42821     // blacklist + whitelisted elements..
42822     black: false,
42823     white: false,
42824      
42825     
42826
42827     /**
42828      * Protected method that will not generally be called directly. It
42829      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42830      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42831      */
42832     getDocMarkup : function(){
42833         // body styles..
42834         var st = '';
42835         
42836         // inherit styels from page...?? 
42837         if (this.stylesheets === false) {
42838             
42839             Roo.get(document.head).select('style').each(function(node) {
42840                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42841             });
42842             
42843             Roo.get(document.head).select('link').each(function(node) { 
42844                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42845             });
42846             
42847         } else if (!this.stylesheets.length) {
42848                 // simple..
42849                 st = '<style type="text/css">' +
42850                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42851                    '</style>';
42852         } else { 
42853             
42854         }
42855         
42856         st +=  '<style type="text/css">' +
42857             'IMG { cursor: pointer } ' +
42858         '</style>';
42859
42860         
42861         return '<html><head>' + st  +
42862             //<style type="text/css">' +
42863             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42864             //'</style>' +
42865             ' </head><body class="roo-htmleditor-body"></body></html>';
42866     },
42867
42868     // private
42869     onRender : function(ct, position)
42870     {
42871         var _t = this;
42872         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42873         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42874         
42875         
42876         this.el.dom.style.border = '0 none';
42877         this.el.dom.setAttribute('tabIndex', -1);
42878         this.el.addClass('x-hidden hide');
42879         
42880         
42881         
42882         if(Roo.isIE){ // fix IE 1px bogus margin
42883             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
42884         }
42885        
42886         
42887         this.frameId = Roo.id();
42888         
42889          
42890         
42891         var iframe = this.owner.wrap.createChild({
42892             tag: 'iframe',
42893             cls: 'form-control', // bootstrap..
42894             id: this.frameId,
42895             name: this.frameId,
42896             frameBorder : 'no',
42897             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
42898         }, this.el
42899         );
42900         
42901         
42902         this.iframe = iframe.dom;
42903
42904          this.assignDocWin();
42905         
42906         this.doc.designMode = 'on';
42907        
42908         this.doc.open();
42909         this.doc.write(this.getDocMarkup());
42910         this.doc.close();
42911
42912         
42913         var task = { // must defer to wait for browser to be ready
42914             run : function(){
42915                 //console.log("run task?" + this.doc.readyState);
42916                 this.assignDocWin();
42917                 if(this.doc.body || this.doc.readyState == 'complete'){
42918                     try {
42919                         this.doc.designMode="on";
42920                     } catch (e) {
42921                         return;
42922                     }
42923                     Roo.TaskMgr.stop(task);
42924                     this.initEditor.defer(10, this);
42925                 }
42926             },
42927             interval : 10,
42928             duration: 10000,
42929             scope: this
42930         };
42931         Roo.TaskMgr.start(task);
42932
42933     },
42934
42935     // private
42936     onResize : function(w, h)
42937     {
42938          Roo.log('resize: ' +w + ',' + h );
42939         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
42940         if(!this.iframe){
42941             return;
42942         }
42943         if(typeof w == 'number'){
42944             
42945             this.iframe.style.width = w + 'px';
42946         }
42947         if(typeof h == 'number'){
42948             
42949             this.iframe.style.height = h + 'px';
42950             if(this.doc){
42951                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
42952             }
42953         }
42954         
42955     },
42956
42957     /**
42958      * Toggles the editor between standard and source edit mode.
42959      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42960      */
42961     toggleSourceEdit : function(sourceEditMode){
42962         
42963         this.sourceEditMode = sourceEditMode === true;
42964         
42965         if(this.sourceEditMode){
42966  
42967             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
42968             
42969         }else{
42970             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
42971             //this.iframe.className = '';
42972             this.deferFocus();
42973         }
42974         //this.setSize(this.owner.wrap.getSize());
42975         //this.fireEvent('editmodechange', this, this.sourceEditMode);
42976     },
42977
42978     
42979   
42980
42981     /**
42982      * Protected method that will not generally be called directly. If you need/want
42983      * custom HTML cleanup, this is the method you should override.
42984      * @param {String} html The HTML to be cleaned
42985      * return {String} The cleaned HTML
42986      */
42987     cleanHtml : function(html){
42988         html = String(html);
42989         if(html.length > 5){
42990             if(Roo.isSafari){ // strip safari nonsense
42991                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
42992             }
42993         }
42994         if(html == '&nbsp;'){
42995             html = '';
42996         }
42997         return html;
42998     },
42999
43000     /**
43001      * HTML Editor -> Textarea
43002      * Protected method that will not generally be called directly. Syncs the contents
43003      * of the editor iframe with the textarea.
43004      */
43005     syncValue : function(){
43006         if(this.initialized){
43007             var bd = (this.doc.body || this.doc.documentElement);
43008             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43009             var html = bd.innerHTML;
43010             if(Roo.isSafari){
43011                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43012                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43013                 if(m && m[1]){
43014                     html = '<div style="'+m[0]+'">' + html + '</div>';
43015                 }
43016             }
43017             html = this.cleanHtml(html);
43018             // fix up the special chars.. normaly like back quotes in word...
43019             // however we do not want to do this with chinese..
43020             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
43021                 var cc = b.charCodeAt();
43022                 if (
43023                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43024                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43025                     (cc >= 0xf900 && cc < 0xfb00 )
43026                 ) {
43027                         return b;
43028                 }
43029                 return "&#"+cc+";" 
43030             });
43031             if(this.owner.fireEvent('beforesync', this, html) !== false){
43032                 this.el.dom.value = html;
43033                 this.owner.fireEvent('sync', this, html);
43034             }
43035         }
43036     },
43037
43038     /**
43039      * Protected method that will not generally be called directly. Pushes the value of the textarea
43040      * into the iframe editor.
43041      */
43042     pushValue : function(){
43043         if(this.initialized){
43044             var v = this.el.dom.value.trim();
43045             
43046 //            if(v.length < 1){
43047 //                v = '&#160;';
43048 //            }
43049             
43050             if(this.owner.fireEvent('beforepush', this, v) !== false){
43051                 var d = (this.doc.body || this.doc.documentElement);
43052                 d.innerHTML = v;
43053                 this.cleanUpPaste();
43054                 this.el.dom.value = d.innerHTML;
43055                 this.owner.fireEvent('push', this, v);
43056             }
43057         }
43058     },
43059
43060     // private
43061     deferFocus : function(){
43062         this.focus.defer(10, this);
43063     },
43064
43065     // doc'ed in Field
43066     focus : function(){
43067         if(this.win && !this.sourceEditMode){
43068             this.win.focus();
43069         }else{
43070             this.el.focus();
43071         }
43072     },
43073     
43074     assignDocWin: function()
43075     {
43076         var iframe = this.iframe;
43077         
43078          if(Roo.isIE){
43079             this.doc = iframe.contentWindow.document;
43080             this.win = iframe.contentWindow;
43081         } else {
43082 //            if (!Roo.get(this.frameId)) {
43083 //                return;
43084 //            }
43085 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43086 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43087             
43088             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43089                 return;
43090             }
43091             
43092             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43093             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43094         }
43095     },
43096     
43097     // private
43098     initEditor : function(){
43099         //console.log("INIT EDITOR");
43100         this.assignDocWin();
43101         
43102         
43103         
43104         this.doc.designMode="on";
43105         this.doc.open();
43106         this.doc.write(this.getDocMarkup());
43107         this.doc.close();
43108         
43109         var dbody = (this.doc.body || this.doc.documentElement);
43110         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43111         // this copies styles from the containing element into thsi one..
43112         // not sure why we need all of this..
43113         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43114         
43115         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43116         //ss['background-attachment'] = 'fixed'; // w3c
43117         dbody.bgProperties = 'fixed'; // ie
43118         //Roo.DomHelper.applyStyles(dbody, ss);
43119         Roo.EventManager.on(this.doc, {
43120             //'mousedown': this.onEditorEvent,
43121             'mouseup': this.onEditorEvent,
43122             'dblclick': this.onEditorEvent,
43123             'click': this.onEditorEvent,
43124             'keyup': this.onEditorEvent,
43125             buffer:100,
43126             scope: this
43127         });
43128         if(Roo.isGecko){
43129             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43130         }
43131         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43132             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43133         }
43134         this.initialized = true;
43135
43136         this.owner.fireEvent('initialize', this);
43137         this.pushValue();
43138     },
43139
43140     // private
43141     onDestroy : function(){
43142         
43143         
43144         
43145         if(this.rendered){
43146             
43147             //for (var i =0; i < this.toolbars.length;i++) {
43148             //    // fixme - ask toolbars for heights?
43149             //    this.toolbars[i].onDestroy();
43150            // }
43151             
43152             //this.wrap.dom.innerHTML = '';
43153             //this.wrap.remove();
43154         }
43155     },
43156
43157     // private
43158     onFirstFocus : function(){
43159         
43160         this.assignDocWin();
43161         
43162         
43163         this.activated = true;
43164          
43165     
43166         if(Roo.isGecko){ // prevent silly gecko errors
43167             this.win.focus();
43168             var s = this.win.getSelection();
43169             if(!s.focusNode || s.focusNode.nodeType != 3){
43170                 var r = s.getRangeAt(0);
43171                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43172                 r.collapse(true);
43173                 this.deferFocus();
43174             }
43175             try{
43176                 this.execCmd('useCSS', true);
43177                 this.execCmd('styleWithCSS', false);
43178             }catch(e){}
43179         }
43180         this.owner.fireEvent('activate', this);
43181     },
43182
43183     // private
43184     adjustFont: function(btn){
43185         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43186         //if(Roo.isSafari){ // safari
43187         //    adjust *= 2;
43188        // }
43189         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43190         if(Roo.isSafari){ // safari
43191             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43192             v =  (v < 10) ? 10 : v;
43193             v =  (v > 48) ? 48 : v;
43194             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43195             
43196         }
43197         
43198         
43199         v = Math.max(1, v+adjust);
43200         
43201         this.execCmd('FontSize', v  );
43202     },
43203
43204     onEditorEvent : function(e)
43205     {
43206         this.owner.fireEvent('editorevent', this, e);
43207       //  this.updateToolbar();
43208         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43209     },
43210
43211     insertTag : function(tg)
43212     {
43213         // could be a bit smarter... -> wrap the current selected tRoo..
43214         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43215             
43216             range = this.createRange(this.getSelection());
43217             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43218             wrappingNode.appendChild(range.extractContents());
43219             range.insertNode(wrappingNode);
43220
43221             return;
43222             
43223             
43224             
43225         }
43226         this.execCmd("formatblock",   tg);
43227         
43228     },
43229     
43230     insertText : function(txt)
43231     {
43232         
43233         
43234         var range = this.createRange();
43235         range.deleteContents();
43236                //alert(Sender.getAttribute('label'));
43237                
43238         range.insertNode(this.doc.createTextNode(txt));
43239     } ,
43240     
43241      
43242
43243     /**
43244      * Executes a Midas editor command on the editor document and performs necessary focus and
43245      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43246      * @param {String} cmd The Midas command
43247      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43248      */
43249     relayCmd : function(cmd, value){
43250         this.win.focus();
43251         this.execCmd(cmd, value);
43252         this.owner.fireEvent('editorevent', this);
43253         //this.updateToolbar();
43254         this.owner.deferFocus();
43255     },
43256
43257     /**
43258      * Executes a Midas editor command directly on the editor document.
43259      * For visual commands, you should use {@link #relayCmd} instead.
43260      * <b>This should only be called after the editor is initialized.</b>
43261      * @param {String} cmd The Midas command
43262      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43263      */
43264     execCmd : function(cmd, value){
43265         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43266         this.syncValue();
43267     },
43268  
43269  
43270    
43271     /**
43272      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43273      * to insert tRoo.
43274      * @param {String} text | dom node.. 
43275      */
43276     insertAtCursor : function(text)
43277     {
43278         
43279         if(!this.activated){
43280             return;
43281         }
43282         /*
43283         if(Roo.isIE){
43284             this.win.focus();
43285             var r = this.doc.selection.createRange();
43286             if(r){
43287                 r.collapse(true);
43288                 r.pasteHTML(text);
43289                 this.syncValue();
43290                 this.deferFocus();
43291             
43292             }
43293             return;
43294         }
43295         */
43296         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43297             this.win.focus();
43298             
43299             
43300             // from jquery ui (MIT licenced)
43301             var range, node;
43302             var win = this.win;
43303             
43304             if (win.getSelection && win.getSelection().getRangeAt) {
43305                 range = win.getSelection().getRangeAt(0);
43306                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43307                 range.insertNode(node);
43308             } else if (win.document.selection && win.document.selection.createRange) {
43309                 // no firefox support
43310                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43311                 win.document.selection.createRange().pasteHTML(txt);
43312             } else {
43313                 // no firefox support
43314                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43315                 this.execCmd('InsertHTML', txt);
43316             } 
43317             
43318             this.syncValue();
43319             
43320             this.deferFocus();
43321         }
43322     },
43323  // private
43324     mozKeyPress : function(e){
43325         if(e.ctrlKey){
43326             var c = e.getCharCode(), cmd;
43327           
43328             if(c > 0){
43329                 c = String.fromCharCode(c).toLowerCase();
43330                 switch(c){
43331                     case 'b':
43332                         cmd = 'bold';
43333                         break;
43334                     case 'i':
43335                         cmd = 'italic';
43336                         break;
43337                     
43338                     case 'u':
43339                         cmd = 'underline';
43340                         break;
43341                     
43342                     case 'v':
43343                         this.cleanUpPaste.defer(100, this);
43344                         return;
43345                         
43346                 }
43347                 if(cmd){
43348                     this.win.focus();
43349                     this.execCmd(cmd);
43350                     this.deferFocus();
43351                     e.preventDefault();
43352                 }
43353                 
43354             }
43355         }
43356     },
43357
43358     // private
43359     fixKeys : function(){ // load time branching for fastest keydown performance
43360         if(Roo.isIE){
43361             return function(e){
43362                 var k = e.getKey(), r;
43363                 if(k == e.TAB){
43364                     e.stopEvent();
43365                     r = this.doc.selection.createRange();
43366                     if(r){
43367                         r.collapse(true);
43368                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43369                         this.deferFocus();
43370                     }
43371                     return;
43372                 }
43373                 
43374                 if(k == e.ENTER){
43375                     r = this.doc.selection.createRange();
43376                     if(r){
43377                         var target = r.parentElement();
43378                         if(!target || target.tagName.toLowerCase() != 'li'){
43379                             e.stopEvent();
43380                             r.pasteHTML('<br />');
43381                             r.collapse(false);
43382                             r.select();
43383                         }
43384                     }
43385                 }
43386                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43387                     this.cleanUpPaste.defer(100, this);
43388                     return;
43389                 }
43390                 
43391                 
43392             };
43393         }else if(Roo.isOpera){
43394             return function(e){
43395                 var k = e.getKey();
43396                 if(k == e.TAB){
43397                     e.stopEvent();
43398                     this.win.focus();
43399                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43400                     this.deferFocus();
43401                 }
43402                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43403                     this.cleanUpPaste.defer(100, this);
43404                     return;
43405                 }
43406                 
43407             };
43408         }else if(Roo.isSafari){
43409             return function(e){
43410                 var k = e.getKey();
43411                 
43412                 if(k == e.TAB){
43413                     e.stopEvent();
43414                     this.execCmd('InsertText','\t');
43415                     this.deferFocus();
43416                     return;
43417                 }
43418                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43419                     this.cleanUpPaste.defer(100, this);
43420                     return;
43421                 }
43422                 
43423              };
43424         }
43425     }(),
43426     
43427     getAllAncestors: function()
43428     {
43429         var p = this.getSelectedNode();
43430         var a = [];
43431         if (!p) {
43432             a.push(p); // push blank onto stack..
43433             p = this.getParentElement();
43434         }
43435         
43436         
43437         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43438             a.push(p);
43439             p = p.parentNode;
43440         }
43441         a.push(this.doc.body);
43442         return a;
43443     },
43444     lastSel : false,
43445     lastSelNode : false,
43446     
43447     
43448     getSelection : function() 
43449     {
43450         this.assignDocWin();
43451         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43452     },
43453     
43454     getSelectedNode: function() 
43455     {
43456         // this may only work on Gecko!!!
43457         
43458         // should we cache this!!!!
43459         
43460         
43461         
43462          
43463         var range = this.createRange(this.getSelection()).cloneRange();
43464         
43465         if (Roo.isIE) {
43466             var parent = range.parentElement();
43467             while (true) {
43468                 var testRange = range.duplicate();
43469                 testRange.moveToElementText(parent);
43470                 if (testRange.inRange(range)) {
43471                     break;
43472                 }
43473                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43474                     break;
43475                 }
43476                 parent = parent.parentElement;
43477             }
43478             return parent;
43479         }
43480         
43481         // is ancestor a text element.
43482         var ac =  range.commonAncestorContainer;
43483         if (ac.nodeType == 3) {
43484             ac = ac.parentNode;
43485         }
43486         
43487         var ar = ac.childNodes;
43488          
43489         var nodes = [];
43490         var other_nodes = [];
43491         var has_other_nodes = false;
43492         for (var i=0;i<ar.length;i++) {
43493             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43494                 continue;
43495             }
43496             // fullly contained node.
43497             
43498             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43499                 nodes.push(ar[i]);
43500                 continue;
43501             }
43502             
43503             // probably selected..
43504             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43505                 other_nodes.push(ar[i]);
43506                 continue;
43507             }
43508             // outer..
43509             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43510                 continue;
43511             }
43512             
43513             
43514             has_other_nodes = true;
43515         }
43516         if (!nodes.length && other_nodes.length) {
43517             nodes= other_nodes;
43518         }
43519         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43520             return false;
43521         }
43522         
43523         return nodes[0];
43524     },
43525     createRange: function(sel)
43526     {
43527         // this has strange effects when using with 
43528         // top toolbar - not sure if it's a great idea.
43529         //this.editor.contentWindow.focus();
43530         if (typeof sel != "undefined") {
43531             try {
43532                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43533             } catch(e) {
43534                 return this.doc.createRange();
43535             }
43536         } else {
43537             return this.doc.createRange();
43538         }
43539     },
43540     getParentElement: function()
43541     {
43542         
43543         this.assignDocWin();
43544         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43545         
43546         var range = this.createRange(sel);
43547          
43548         try {
43549             var p = range.commonAncestorContainer;
43550             while (p.nodeType == 3) { // text node
43551                 p = p.parentNode;
43552             }
43553             return p;
43554         } catch (e) {
43555             return null;
43556         }
43557     
43558     },
43559     /***
43560      *
43561      * Range intersection.. the hard stuff...
43562      *  '-1' = before
43563      *  '0' = hits..
43564      *  '1' = after.
43565      *         [ -- selected range --- ]
43566      *   [fail]                        [fail]
43567      *
43568      *    basically..
43569      *      if end is before start or  hits it. fail.
43570      *      if start is after end or hits it fail.
43571      *
43572      *   if either hits (but other is outside. - then it's not 
43573      *   
43574      *    
43575      **/
43576     
43577     
43578     // @see http://www.thismuchiknow.co.uk/?p=64.
43579     rangeIntersectsNode : function(range, node)
43580     {
43581         var nodeRange = node.ownerDocument.createRange();
43582         try {
43583             nodeRange.selectNode(node);
43584         } catch (e) {
43585             nodeRange.selectNodeContents(node);
43586         }
43587     
43588         var rangeStartRange = range.cloneRange();
43589         rangeStartRange.collapse(true);
43590     
43591         var rangeEndRange = range.cloneRange();
43592         rangeEndRange.collapse(false);
43593     
43594         var nodeStartRange = nodeRange.cloneRange();
43595         nodeStartRange.collapse(true);
43596     
43597         var nodeEndRange = nodeRange.cloneRange();
43598         nodeEndRange.collapse(false);
43599     
43600         return rangeStartRange.compareBoundaryPoints(
43601                  Range.START_TO_START, nodeEndRange) == -1 &&
43602                rangeEndRange.compareBoundaryPoints(
43603                  Range.START_TO_START, nodeStartRange) == 1;
43604         
43605          
43606     },
43607     rangeCompareNode : function(range, node)
43608     {
43609         var nodeRange = node.ownerDocument.createRange();
43610         try {
43611             nodeRange.selectNode(node);
43612         } catch (e) {
43613             nodeRange.selectNodeContents(node);
43614         }
43615         
43616         
43617         range.collapse(true);
43618     
43619         nodeRange.collapse(true);
43620      
43621         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43622         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43623          
43624         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43625         
43626         var nodeIsBefore   =  ss == 1;
43627         var nodeIsAfter    = ee == -1;
43628         
43629         if (nodeIsBefore && nodeIsAfter) {
43630             return 0; // outer
43631         }
43632         if (!nodeIsBefore && nodeIsAfter) {
43633             return 1; //right trailed.
43634         }
43635         
43636         if (nodeIsBefore && !nodeIsAfter) {
43637             return 2;  // left trailed.
43638         }
43639         // fully contined.
43640         return 3;
43641     },
43642
43643     // private? - in a new class?
43644     cleanUpPaste :  function()
43645     {
43646         // cleans up the whole document..
43647         Roo.log('cleanuppaste');
43648         
43649         this.cleanUpChildren(this.doc.body);
43650         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43651         if (clean != this.doc.body.innerHTML) {
43652             this.doc.body.innerHTML = clean;
43653         }
43654         
43655     },
43656     
43657     cleanWordChars : function(input) {// change the chars to hex code
43658         var he = Roo.HtmlEditorCore;
43659         
43660         var output = input;
43661         Roo.each(he.swapCodes, function(sw) { 
43662             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43663             
43664             output = output.replace(swapper, sw[1]);
43665         });
43666         
43667         return output;
43668     },
43669     
43670     
43671     cleanUpChildren : function (n)
43672     {
43673         if (!n.childNodes.length) {
43674             return;
43675         }
43676         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43677            this.cleanUpChild(n.childNodes[i]);
43678         }
43679     },
43680     
43681     
43682         
43683     
43684     cleanUpChild : function (node)
43685     {
43686         var ed = this;
43687         //console.log(node);
43688         if (node.nodeName == "#text") {
43689             // clean up silly Windows -- stuff?
43690             return; 
43691         }
43692         if (node.nodeName == "#comment") {
43693             node.parentNode.removeChild(node);
43694             // clean up silly Windows -- stuff?
43695             return; 
43696         }
43697         var lcname = node.tagName.toLowerCase();
43698         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43699         // whitelist of tags..
43700         
43701         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43702             // remove node.
43703             node.parentNode.removeChild(node);
43704             return;
43705             
43706         }
43707         
43708         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43709         
43710         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43711         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43712         
43713         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43714         //    remove_keep_children = true;
43715         //}
43716         
43717         if (remove_keep_children) {
43718             this.cleanUpChildren(node);
43719             // inserts everything just before this node...
43720             while (node.childNodes.length) {
43721                 var cn = node.childNodes[0];
43722                 node.removeChild(cn);
43723                 node.parentNode.insertBefore(cn, node);
43724             }
43725             node.parentNode.removeChild(node);
43726             return;
43727         }
43728         
43729         if (!node.attributes || !node.attributes.length) {
43730             this.cleanUpChildren(node);
43731             return;
43732         }
43733         
43734         function cleanAttr(n,v)
43735         {
43736             
43737             if (v.match(/^\./) || v.match(/^\//)) {
43738                 return;
43739             }
43740             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
43741                 return;
43742             }
43743             if (v.match(/^#/)) {
43744                 return;
43745             }
43746 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43747             node.removeAttribute(n);
43748             
43749         }
43750         
43751         var cwhite = this.cwhite;
43752         var cblack = this.cblack;
43753             
43754         function cleanStyle(n,v)
43755         {
43756             if (v.match(/expression/)) { //XSS?? should we even bother..
43757                 node.removeAttribute(n);
43758                 return;
43759             }
43760             
43761             var parts = v.split(/;/);
43762             var clean = [];
43763             
43764             Roo.each(parts, function(p) {
43765                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43766                 if (!p.length) {
43767                     return true;
43768                 }
43769                 var l = p.split(':').shift().replace(/\s+/g,'');
43770                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43771                 
43772                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43773 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43774                     //node.removeAttribute(n);
43775                     return true;
43776                 }
43777                 //Roo.log()
43778                 // only allow 'c whitelisted system attributes'
43779                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43780 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43781                     //node.removeAttribute(n);
43782                     return true;
43783                 }
43784                 
43785                 
43786                  
43787                 
43788                 clean.push(p);
43789                 return true;
43790             });
43791             if (clean.length) { 
43792                 node.setAttribute(n, clean.join(';'));
43793             } else {
43794                 node.removeAttribute(n);
43795             }
43796             
43797         }
43798         
43799         
43800         for (var i = node.attributes.length-1; i > -1 ; i--) {
43801             var a = node.attributes[i];
43802             //console.log(a);
43803             
43804             if (a.name.toLowerCase().substr(0,2)=='on')  {
43805                 node.removeAttribute(a.name);
43806                 continue;
43807             }
43808             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43809                 node.removeAttribute(a.name);
43810                 continue;
43811             }
43812             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43813                 cleanAttr(a.name,a.value); // fixme..
43814                 continue;
43815             }
43816             if (a.name == 'style') {
43817                 cleanStyle(a.name,a.value);
43818                 continue;
43819             }
43820             /// clean up MS crap..
43821             // tecnically this should be a list of valid class'es..
43822             
43823             
43824             if (a.name == 'class') {
43825                 if (a.value.match(/^Mso/)) {
43826                     node.className = '';
43827                 }
43828                 
43829                 if (a.value.match(/^body$/)) {
43830                     node.className = '';
43831                 }
43832                 continue;
43833             }
43834             
43835             // style cleanup!?
43836             // class cleanup?
43837             
43838         }
43839         
43840         
43841         this.cleanUpChildren(node);
43842         
43843         
43844     },
43845     
43846     /**
43847      * Clean up MS wordisms...
43848      */
43849     cleanWord : function(node)
43850     {
43851         
43852         
43853         if (!node) {
43854             this.cleanWord(this.doc.body);
43855             return;
43856         }
43857         if (node.nodeName == "#text") {
43858             // clean up silly Windows -- stuff?
43859             return; 
43860         }
43861         if (node.nodeName == "#comment") {
43862             node.parentNode.removeChild(node);
43863             // clean up silly Windows -- stuff?
43864             return; 
43865         }
43866         
43867         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43868             node.parentNode.removeChild(node);
43869             return;
43870         }
43871         
43872         // remove - but keep children..
43873         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43874             while (node.childNodes.length) {
43875                 var cn = node.childNodes[0];
43876                 node.removeChild(cn);
43877                 node.parentNode.insertBefore(cn, node);
43878             }
43879             node.parentNode.removeChild(node);
43880             this.iterateChildren(node, this.cleanWord);
43881             return;
43882         }
43883         // clean styles
43884         if (node.className.length) {
43885             
43886             var cn = node.className.split(/\W+/);
43887             var cna = [];
43888             Roo.each(cn, function(cls) {
43889                 if (cls.match(/Mso[a-zA-Z]+/)) {
43890                     return;
43891                 }
43892                 cna.push(cls);
43893             });
43894             node.className = cna.length ? cna.join(' ') : '';
43895             if (!cna.length) {
43896                 node.removeAttribute("class");
43897             }
43898         }
43899         
43900         if (node.hasAttribute("lang")) {
43901             node.removeAttribute("lang");
43902         }
43903         
43904         if (node.hasAttribute("style")) {
43905             
43906             var styles = node.getAttribute("style").split(";");
43907             var nstyle = [];
43908             Roo.each(styles, function(s) {
43909                 if (!s.match(/:/)) {
43910                     return;
43911                 }
43912                 var kv = s.split(":");
43913                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
43914                     return;
43915                 }
43916                 // what ever is left... we allow.
43917                 nstyle.push(s);
43918             });
43919             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43920             if (!nstyle.length) {
43921                 node.removeAttribute('style');
43922             }
43923         }
43924         this.iterateChildren(node, this.cleanWord);
43925         
43926         
43927         
43928     },
43929     /**
43930      * iterateChildren of a Node, calling fn each time, using this as the scole..
43931      * @param {DomNode} node node to iterate children of.
43932      * @param {Function} fn method of this class to call on each item.
43933      */
43934     iterateChildren : function(node, fn)
43935     {
43936         if (!node.childNodes.length) {
43937                 return;
43938         }
43939         for (var i = node.childNodes.length-1; i > -1 ; i--) {
43940            fn.call(this, node.childNodes[i])
43941         }
43942     },
43943     
43944     
43945     /**
43946      * cleanTableWidths.
43947      *
43948      * Quite often pasting from word etc.. results in tables with column and widths.
43949      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
43950      *
43951      */
43952     cleanTableWidths : function(node)
43953     {
43954          
43955          
43956         if (!node) {
43957             this.cleanTableWidths(this.doc.body);
43958             return;
43959         }
43960         
43961         // ignore list...
43962         if (node.nodeName == "#text" || node.nodeName == "#comment") {
43963             return; 
43964         }
43965         Roo.log(node.tagName);
43966         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
43967             this.iterateChildren(node, this.cleanTableWidths);
43968             return;
43969         }
43970         if (node.hasAttribute('width')) {
43971             node.removeAttribute('width');
43972         }
43973         
43974          
43975         if (node.hasAttribute("style")) {
43976             // pretty basic...
43977             
43978             var styles = node.getAttribute("style").split(";");
43979             var nstyle = [];
43980             Roo.each(styles, function(s) {
43981                 if (!s.match(/:/)) {
43982                     return;
43983                 }
43984                 var kv = s.split(":");
43985                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
43986                     return;
43987                 }
43988                 // what ever is left... we allow.
43989                 nstyle.push(s);
43990             });
43991             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43992             if (!nstyle.length) {
43993                 node.removeAttribute('style');
43994             }
43995         }
43996         
43997         this.iterateChildren(node, this.cleanTableWidths);
43998         
43999         
44000     },
44001     
44002     
44003     
44004     
44005     domToHTML : function(currentElement, depth, nopadtext) {
44006         
44007         depth = depth || 0;
44008         nopadtext = nopadtext || false;
44009     
44010         if (!currentElement) {
44011             return this.domToHTML(this.doc.body);
44012         }
44013         
44014         //Roo.log(currentElement);
44015         var j;
44016         var allText = false;
44017         var nodeName = currentElement.nodeName;
44018         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44019         
44020         if  (nodeName == '#text') {
44021             
44022             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44023         }
44024         
44025         
44026         var ret = '';
44027         if (nodeName != 'BODY') {
44028              
44029             var i = 0;
44030             // Prints the node tagName, such as <A>, <IMG>, etc
44031             if (tagName) {
44032                 var attr = [];
44033                 for(i = 0; i < currentElement.attributes.length;i++) {
44034                     // quoting?
44035                     var aname = currentElement.attributes.item(i).name;
44036                     if (!currentElement.attributes.item(i).value.length) {
44037                         continue;
44038                     }
44039                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44040                 }
44041                 
44042                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44043             } 
44044             else {
44045                 
44046                 // eack
44047             }
44048         } else {
44049             tagName = false;
44050         }
44051         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44052             return ret;
44053         }
44054         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44055             nopadtext = true;
44056         }
44057         
44058         
44059         // Traverse the tree
44060         i = 0;
44061         var currentElementChild = currentElement.childNodes.item(i);
44062         var allText = true;
44063         var innerHTML  = '';
44064         lastnode = '';
44065         while (currentElementChild) {
44066             // Formatting code (indent the tree so it looks nice on the screen)
44067             var nopad = nopadtext;
44068             if (lastnode == 'SPAN') {
44069                 nopad  = true;
44070             }
44071             // text
44072             if  (currentElementChild.nodeName == '#text') {
44073                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44074                 toadd = nopadtext ? toadd : toadd.trim();
44075                 if (!nopad && toadd.length > 80) {
44076                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44077                 }
44078                 innerHTML  += toadd;
44079                 
44080                 i++;
44081                 currentElementChild = currentElement.childNodes.item(i);
44082                 lastNode = '';
44083                 continue;
44084             }
44085             allText = false;
44086             
44087             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44088                 
44089             // Recursively traverse the tree structure of the child node
44090             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44091             lastnode = currentElementChild.nodeName;
44092             i++;
44093             currentElementChild=currentElement.childNodes.item(i);
44094         }
44095         
44096         ret += innerHTML;
44097         
44098         if (!allText) {
44099                 // The remaining code is mostly for formatting the tree
44100             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44101         }
44102         
44103         
44104         if (tagName) {
44105             ret+= "</"+tagName+">";
44106         }
44107         return ret;
44108         
44109     },
44110         
44111     applyBlacklists : function()
44112     {
44113         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44114         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44115         
44116         this.white = [];
44117         this.black = [];
44118         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44119             if (b.indexOf(tag) > -1) {
44120                 return;
44121             }
44122             this.white.push(tag);
44123             
44124         }, this);
44125         
44126         Roo.each(w, function(tag) {
44127             if (b.indexOf(tag) > -1) {
44128                 return;
44129             }
44130             if (this.white.indexOf(tag) > -1) {
44131                 return;
44132             }
44133             this.white.push(tag);
44134             
44135         }, this);
44136         
44137         
44138         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44139             if (w.indexOf(tag) > -1) {
44140                 return;
44141             }
44142             this.black.push(tag);
44143             
44144         }, this);
44145         
44146         Roo.each(b, function(tag) {
44147             if (w.indexOf(tag) > -1) {
44148                 return;
44149             }
44150             if (this.black.indexOf(tag) > -1) {
44151                 return;
44152             }
44153             this.black.push(tag);
44154             
44155         }, this);
44156         
44157         
44158         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44159         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44160         
44161         this.cwhite = [];
44162         this.cblack = [];
44163         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44164             if (b.indexOf(tag) > -1) {
44165                 return;
44166             }
44167             this.cwhite.push(tag);
44168             
44169         }, this);
44170         
44171         Roo.each(w, function(tag) {
44172             if (b.indexOf(tag) > -1) {
44173                 return;
44174             }
44175             if (this.cwhite.indexOf(tag) > -1) {
44176                 return;
44177             }
44178             this.cwhite.push(tag);
44179             
44180         }, this);
44181         
44182         
44183         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44184             if (w.indexOf(tag) > -1) {
44185                 return;
44186             }
44187             this.cblack.push(tag);
44188             
44189         }, this);
44190         
44191         Roo.each(b, function(tag) {
44192             if (w.indexOf(tag) > -1) {
44193                 return;
44194             }
44195             if (this.cblack.indexOf(tag) > -1) {
44196                 return;
44197             }
44198             this.cblack.push(tag);
44199             
44200         }, this);
44201     },
44202     
44203     setStylesheets : function(stylesheets)
44204     {
44205         if(typeof(stylesheets) == 'string'){
44206             Roo.get(this.iframe.contentDocument.head).createChild({
44207                 tag : 'link',
44208                 rel : 'stylesheet',
44209                 type : 'text/css',
44210                 href : stylesheets
44211             });
44212             
44213             return;
44214         }
44215         var _this = this;
44216      
44217         Roo.each(stylesheets, function(s) {
44218             if(!s.length){
44219                 return;
44220             }
44221             
44222             Roo.get(_this.iframe.contentDocument.head).createChild({
44223                 tag : 'link',
44224                 rel : 'stylesheet',
44225                 type : 'text/css',
44226                 href : s
44227             });
44228         });
44229
44230         
44231     },
44232     
44233     removeStylesheets : function()
44234     {
44235         var _this = this;
44236         
44237         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44238             s.remove();
44239         });
44240     }
44241     
44242     // hide stuff that is not compatible
44243     /**
44244      * @event blur
44245      * @hide
44246      */
44247     /**
44248      * @event change
44249      * @hide
44250      */
44251     /**
44252      * @event focus
44253      * @hide
44254      */
44255     /**
44256      * @event specialkey
44257      * @hide
44258      */
44259     /**
44260      * @cfg {String} fieldClass @hide
44261      */
44262     /**
44263      * @cfg {String} focusClass @hide
44264      */
44265     /**
44266      * @cfg {String} autoCreate @hide
44267      */
44268     /**
44269      * @cfg {String} inputType @hide
44270      */
44271     /**
44272      * @cfg {String} invalidClass @hide
44273      */
44274     /**
44275      * @cfg {String} invalidText @hide
44276      */
44277     /**
44278      * @cfg {String} msgFx @hide
44279      */
44280     /**
44281      * @cfg {String} validateOnBlur @hide
44282      */
44283 });
44284
44285 Roo.HtmlEditorCore.white = [
44286         'area', 'br', 'img', 'input', 'hr', 'wbr',
44287         
44288        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44289        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44290        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44291        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44292        'table',   'ul',         'xmp', 
44293        
44294        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44295       'thead',   'tr', 
44296      
44297       'dir', 'menu', 'ol', 'ul', 'dl',
44298        
44299       'embed',  'object'
44300 ];
44301
44302
44303 Roo.HtmlEditorCore.black = [
44304     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44305         'applet', // 
44306         'base',   'basefont', 'bgsound', 'blink',  'body', 
44307         'frame',  'frameset', 'head',    'html',   'ilayer', 
44308         'iframe', 'layer',  'link',     'meta',    'object',   
44309         'script', 'style' ,'title',  'xml' // clean later..
44310 ];
44311 Roo.HtmlEditorCore.clean = [
44312     'script', 'style', 'title', 'xml'
44313 ];
44314 Roo.HtmlEditorCore.remove = [
44315     'font'
44316 ];
44317 // attributes..
44318
44319 Roo.HtmlEditorCore.ablack = [
44320     'on'
44321 ];
44322     
44323 Roo.HtmlEditorCore.aclean = [ 
44324     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44325 ];
44326
44327 // protocols..
44328 Roo.HtmlEditorCore.pwhite= [
44329         'http',  'https',  'mailto'
44330 ];
44331
44332 // white listed style attributes.
44333 Roo.HtmlEditorCore.cwhite= [
44334       //  'text-align', /// default is to allow most things..
44335       
44336          
44337 //        'font-size'//??
44338 ];
44339
44340 // black listed style attributes.
44341 Roo.HtmlEditorCore.cblack= [
44342       //  'font-size' -- this can be set by the project 
44343 ];
44344
44345
44346 Roo.HtmlEditorCore.swapCodes   =[ 
44347     [    8211, "--" ], 
44348     [    8212, "--" ], 
44349     [    8216,  "'" ],  
44350     [    8217, "'" ],  
44351     [    8220, '"' ],  
44352     [    8221, '"' ],  
44353     [    8226, "*" ],  
44354     [    8230, "..." ]
44355 ]; 
44356
44357     //<script type="text/javascript">
44358
44359 /*
44360  * Ext JS Library 1.1.1
44361  * Copyright(c) 2006-2007, Ext JS, LLC.
44362  * Licence LGPL
44363  * 
44364  */
44365  
44366  
44367 Roo.form.HtmlEditor = function(config){
44368     
44369     
44370     
44371     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44372     
44373     if (!this.toolbars) {
44374         this.toolbars = [];
44375     }
44376     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44377     
44378     
44379 };
44380
44381 /**
44382  * @class Roo.form.HtmlEditor
44383  * @extends Roo.form.Field
44384  * Provides a lightweight HTML Editor component.
44385  *
44386  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44387  * 
44388  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44389  * supported by this editor.</b><br/><br/>
44390  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44391  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44392  */
44393 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44394     /**
44395      * @cfg {Boolean} clearUp
44396      */
44397     clearUp : true,
44398       /**
44399      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44400      */
44401     toolbars : false,
44402    
44403      /**
44404      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44405      *                        Roo.resizable.
44406      */
44407     resizable : false,
44408      /**
44409      * @cfg {Number} height (in pixels)
44410      */   
44411     height: 300,
44412    /**
44413      * @cfg {Number} width (in pixels)
44414      */   
44415     width: 500,
44416     
44417     /**
44418      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44419      * 
44420      */
44421     stylesheets: false,
44422     
44423     
44424      /**
44425      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44426      * 
44427      */
44428     cblack: false,
44429     /**
44430      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44431      * 
44432      */
44433     cwhite: false,
44434     
44435      /**
44436      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44437      * 
44438      */
44439     black: false,
44440     /**
44441      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44442      * 
44443      */
44444     white: false,
44445     
44446     // id of frame..
44447     frameId: false,
44448     
44449     // private properties
44450     validationEvent : false,
44451     deferHeight: true,
44452     initialized : false,
44453     activated : false,
44454     
44455     onFocus : Roo.emptyFn,
44456     iframePad:3,
44457     hideMode:'offsets',
44458     
44459     actionMode : 'container', // defaults to hiding it...
44460     
44461     defaultAutoCreate : { // modified by initCompnoent..
44462         tag: "textarea",
44463         style:"width:500px;height:300px;",
44464         autocomplete: "new-password"
44465     },
44466
44467     // private
44468     initComponent : function(){
44469         this.addEvents({
44470             /**
44471              * @event initialize
44472              * Fires when the editor is fully initialized (including the iframe)
44473              * @param {HtmlEditor} this
44474              */
44475             initialize: true,
44476             /**
44477              * @event activate
44478              * Fires when the editor is first receives the focus. Any insertion must wait
44479              * until after this event.
44480              * @param {HtmlEditor} this
44481              */
44482             activate: true,
44483              /**
44484              * @event beforesync
44485              * Fires before the textarea is updated with content from the editor iframe. Return false
44486              * to cancel the sync.
44487              * @param {HtmlEditor} this
44488              * @param {String} html
44489              */
44490             beforesync: true,
44491              /**
44492              * @event beforepush
44493              * Fires before the iframe editor is updated with content from the textarea. Return false
44494              * to cancel the push.
44495              * @param {HtmlEditor} this
44496              * @param {String} html
44497              */
44498             beforepush: true,
44499              /**
44500              * @event sync
44501              * Fires when the textarea is updated with content from the editor iframe.
44502              * @param {HtmlEditor} this
44503              * @param {String} html
44504              */
44505             sync: true,
44506              /**
44507              * @event push
44508              * Fires when the iframe editor is updated with content from the textarea.
44509              * @param {HtmlEditor} this
44510              * @param {String} html
44511              */
44512             push: true,
44513              /**
44514              * @event editmodechange
44515              * Fires when the editor switches edit modes
44516              * @param {HtmlEditor} this
44517              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44518              */
44519             editmodechange: true,
44520             /**
44521              * @event editorevent
44522              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44523              * @param {HtmlEditor} this
44524              */
44525             editorevent: true,
44526             /**
44527              * @event firstfocus
44528              * Fires when on first focus - needed by toolbars..
44529              * @param {HtmlEditor} this
44530              */
44531             firstfocus: true,
44532             /**
44533              * @event autosave
44534              * Auto save the htmlEditor value as a file into Events
44535              * @param {HtmlEditor} this
44536              */
44537             autosave: true,
44538             /**
44539              * @event savedpreview
44540              * preview the saved version of htmlEditor
44541              * @param {HtmlEditor} this
44542              */
44543             savedpreview: true,
44544             
44545             /**
44546             * @event stylesheetsclick
44547             * Fires when press the Sytlesheets button
44548             * @param {Roo.HtmlEditorCore} this
44549             */
44550             stylesheetsclick: true
44551         });
44552         this.defaultAutoCreate =  {
44553             tag: "textarea",
44554             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44555             autocomplete: "new-password"
44556         };
44557     },
44558
44559     /**
44560      * Protected method that will not generally be called directly. It
44561      * is called when the editor creates its toolbar. Override this method if you need to
44562      * add custom toolbar buttons.
44563      * @param {HtmlEditor} editor
44564      */
44565     createToolbar : function(editor){
44566         Roo.log("create toolbars");
44567         if (!editor.toolbars || !editor.toolbars.length) {
44568             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44569         }
44570         
44571         for (var i =0 ; i < editor.toolbars.length;i++) {
44572             editor.toolbars[i] = Roo.factory(
44573                     typeof(editor.toolbars[i]) == 'string' ?
44574                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44575                 Roo.form.HtmlEditor);
44576             editor.toolbars[i].init(editor);
44577         }
44578          
44579         
44580     },
44581
44582      
44583     // private
44584     onRender : function(ct, position)
44585     {
44586         var _t = this;
44587         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44588         
44589         this.wrap = this.el.wrap({
44590             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44591         });
44592         
44593         this.editorcore.onRender(ct, position);
44594          
44595         if (this.resizable) {
44596             this.resizeEl = new Roo.Resizable(this.wrap, {
44597                 pinned : true,
44598                 wrap: true,
44599                 dynamic : true,
44600                 minHeight : this.height,
44601                 height: this.height,
44602                 handles : this.resizable,
44603                 width: this.width,
44604                 listeners : {
44605                     resize : function(r, w, h) {
44606                         _t.onResize(w,h); // -something
44607                     }
44608                 }
44609             });
44610             
44611         }
44612         this.createToolbar(this);
44613        
44614         
44615         if(!this.width){
44616             this.setSize(this.wrap.getSize());
44617         }
44618         if (this.resizeEl) {
44619             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44620             // should trigger onReize..
44621         }
44622         
44623         this.keyNav = new Roo.KeyNav(this.el, {
44624             
44625             "tab" : function(e){
44626                 e.preventDefault();
44627                 
44628                 var value = this.getValue();
44629                 
44630                 var start = this.el.dom.selectionStart;
44631                 var end = this.el.dom.selectionEnd;
44632                 
44633                 if(!e.shiftKey){
44634                     
44635                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44636                     this.el.dom.setSelectionRange(end + 1, end + 1);
44637                     return;
44638                 }
44639                 
44640                 var f = value.substring(0, start).split("\t");
44641                 
44642                 if(f.pop().length != 0){
44643                     return;
44644                 }
44645                 
44646                 this.setValue(f.join("\t") + value.substring(end));
44647                 this.el.dom.setSelectionRange(start - 1, start - 1);
44648                 
44649             },
44650             
44651             "home" : function(e){
44652                 e.preventDefault();
44653                 
44654                 var curr = this.el.dom.selectionStart;
44655                 var lines = this.getValue().split("\n");
44656                 
44657                 if(!lines.length){
44658                     return;
44659                 }
44660                 
44661                 if(e.ctrlKey){
44662                     this.el.dom.setSelectionRange(0, 0);
44663                     return;
44664                 }
44665                 
44666                 var pos = 0;
44667                 
44668                 for (var i = 0; i < lines.length;i++) {
44669                     pos += lines[i].length;
44670                     
44671                     if(i != 0){
44672                         pos += 1;
44673                     }
44674                     
44675                     if(pos < curr){
44676                         continue;
44677                     }
44678                     
44679                     pos -= lines[i].length;
44680                     
44681                     break;
44682                 }
44683                 
44684                 if(!e.shiftKey){
44685                     this.el.dom.setSelectionRange(pos, pos);
44686                     return;
44687                 }
44688                 
44689                 this.el.dom.selectionStart = pos;
44690                 this.el.dom.selectionEnd = curr;
44691             },
44692             
44693             "end" : function(e){
44694                 e.preventDefault();
44695                 
44696                 var curr = this.el.dom.selectionStart;
44697                 var lines = this.getValue().split("\n");
44698                 
44699                 if(!lines.length){
44700                     return;
44701                 }
44702                 
44703                 if(e.ctrlKey){
44704                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44705                     return;
44706                 }
44707                 
44708                 var pos = 0;
44709                 
44710                 for (var i = 0; i < lines.length;i++) {
44711                     
44712                     pos += lines[i].length;
44713                     
44714                     if(i != 0){
44715                         pos += 1;
44716                     }
44717                     
44718                     if(pos < curr){
44719                         continue;
44720                     }
44721                     
44722                     break;
44723                 }
44724                 
44725                 if(!e.shiftKey){
44726                     this.el.dom.setSelectionRange(pos, pos);
44727                     return;
44728                 }
44729                 
44730                 this.el.dom.selectionStart = curr;
44731                 this.el.dom.selectionEnd = pos;
44732             },
44733
44734             scope : this,
44735
44736             doRelay : function(foo, bar, hname){
44737                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44738             },
44739
44740             forceKeyDown: true
44741         });
44742         
44743 //        if(this.autosave && this.w){
44744 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44745 //        }
44746     },
44747
44748     // private
44749     onResize : function(w, h)
44750     {
44751         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44752         var ew = false;
44753         var eh = false;
44754         
44755         if(this.el ){
44756             if(typeof w == 'number'){
44757                 var aw = w - this.wrap.getFrameWidth('lr');
44758                 this.el.setWidth(this.adjustWidth('textarea', aw));
44759                 ew = aw;
44760             }
44761             if(typeof h == 'number'){
44762                 var tbh = 0;
44763                 for (var i =0; i < this.toolbars.length;i++) {
44764                     // fixme - ask toolbars for heights?
44765                     tbh += this.toolbars[i].tb.el.getHeight();
44766                     if (this.toolbars[i].footer) {
44767                         tbh += this.toolbars[i].footer.el.getHeight();
44768                     }
44769                 }
44770                 
44771                 
44772                 
44773                 
44774                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44775                 ah -= 5; // knock a few pixes off for look..
44776 //                Roo.log(ah);
44777                 this.el.setHeight(this.adjustWidth('textarea', ah));
44778                 var eh = ah;
44779             }
44780         }
44781         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44782         this.editorcore.onResize(ew,eh);
44783         
44784     },
44785
44786     /**
44787      * Toggles the editor between standard and source edit mode.
44788      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44789      */
44790     toggleSourceEdit : function(sourceEditMode)
44791     {
44792         this.editorcore.toggleSourceEdit(sourceEditMode);
44793         
44794         if(this.editorcore.sourceEditMode){
44795             Roo.log('editor - showing textarea');
44796             
44797 //            Roo.log('in');
44798 //            Roo.log(this.syncValue());
44799             this.editorcore.syncValue();
44800             this.el.removeClass('x-hidden');
44801             this.el.dom.removeAttribute('tabIndex');
44802             this.el.focus();
44803             
44804             for (var i = 0; i < this.toolbars.length; i++) {
44805                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44806                     this.toolbars[i].tb.hide();
44807                     this.toolbars[i].footer.hide();
44808                 }
44809             }
44810             
44811         }else{
44812             Roo.log('editor - hiding textarea');
44813 //            Roo.log('out')
44814 //            Roo.log(this.pushValue()); 
44815             this.editorcore.pushValue();
44816             
44817             this.el.addClass('x-hidden');
44818             this.el.dom.setAttribute('tabIndex', -1);
44819             
44820             for (var i = 0; i < this.toolbars.length; i++) {
44821                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44822                     this.toolbars[i].tb.show();
44823                     this.toolbars[i].footer.show();
44824                 }
44825             }
44826             
44827             //this.deferFocus();
44828         }
44829         
44830         this.setSize(this.wrap.getSize());
44831         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44832         
44833         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44834     },
44835  
44836     // private (for BoxComponent)
44837     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44838
44839     // private (for BoxComponent)
44840     getResizeEl : function(){
44841         return this.wrap;
44842     },
44843
44844     // private (for BoxComponent)
44845     getPositionEl : function(){
44846         return this.wrap;
44847     },
44848
44849     // private
44850     initEvents : function(){
44851         this.originalValue = this.getValue();
44852     },
44853
44854     /**
44855      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44856      * @method
44857      */
44858     markInvalid : Roo.emptyFn,
44859     /**
44860      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44861      * @method
44862      */
44863     clearInvalid : Roo.emptyFn,
44864
44865     setValue : function(v){
44866         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44867         this.editorcore.pushValue();
44868     },
44869
44870      
44871     // private
44872     deferFocus : function(){
44873         this.focus.defer(10, this);
44874     },
44875
44876     // doc'ed in Field
44877     focus : function(){
44878         this.editorcore.focus();
44879         
44880     },
44881       
44882
44883     // private
44884     onDestroy : function(){
44885         
44886         
44887         
44888         if(this.rendered){
44889             
44890             for (var i =0; i < this.toolbars.length;i++) {
44891                 // fixme - ask toolbars for heights?
44892                 this.toolbars[i].onDestroy();
44893             }
44894             
44895             this.wrap.dom.innerHTML = '';
44896             this.wrap.remove();
44897         }
44898     },
44899
44900     // private
44901     onFirstFocus : function(){
44902         //Roo.log("onFirstFocus");
44903         this.editorcore.onFirstFocus();
44904          for (var i =0; i < this.toolbars.length;i++) {
44905             this.toolbars[i].onFirstFocus();
44906         }
44907         
44908     },
44909     
44910     // private
44911     syncValue : function()
44912     {
44913         this.editorcore.syncValue();
44914     },
44915     
44916     pushValue : function()
44917     {
44918         this.editorcore.pushValue();
44919     },
44920     
44921     setStylesheets : function(stylesheets)
44922     {
44923         this.editorcore.setStylesheets(stylesheets);
44924     },
44925     
44926     removeStylesheets : function()
44927     {
44928         this.editorcore.removeStylesheets();
44929     }
44930      
44931     
44932     // hide stuff that is not compatible
44933     /**
44934      * @event blur
44935      * @hide
44936      */
44937     /**
44938      * @event change
44939      * @hide
44940      */
44941     /**
44942      * @event focus
44943      * @hide
44944      */
44945     /**
44946      * @event specialkey
44947      * @hide
44948      */
44949     /**
44950      * @cfg {String} fieldClass @hide
44951      */
44952     /**
44953      * @cfg {String} focusClass @hide
44954      */
44955     /**
44956      * @cfg {String} autoCreate @hide
44957      */
44958     /**
44959      * @cfg {String} inputType @hide
44960      */
44961     /**
44962      * @cfg {String} invalidClass @hide
44963      */
44964     /**
44965      * @cfg {String} invalidText @hide
44966      */
44967     /**
44968      * @cfg {String} msgFx @hide
44969      */
44970     /**
44971      * @cfg {String} validateOnBlur @hide
44972      */
44973 });
44974  
44975     // <script type="text/javascript">
44976 /*
44977  * Based on
44978  * Ext JS Library 1.1.1
44979  * Copyright(c) 2006-2007, Ext JS, LLC.
44980  *  
44981  
44982  */
44983
44984 /**
44985  * @class Roo.form.HtmlEditorToolbar1
44986  * Basic Toolbar
44987  * 
44988  * Usage:
44989  *
44990  new Roo.form.HtmlEditor({
44991     ....
44992     toolbars : [
44993         new Roo.form.HtmlEditorToolbar1({
44994             disable : { fonts: 1 , format: 1, ..., ... , ...],
44995             btns : [ .... ]
44996         })
44997     }
44998      
44999  * 
45000  * @cfg {Object} disable List of elements to disable..
45001  * @cfg {Array} btns List of additional buttons.
45002  * 
45003  * 
45004  * NEEDS Extra CSS? 
45005  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45006  */
45007  
45008 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45009 {
45010     
45011     Roo.apply(this, config);
45012     
45013     // default disabled, based on 'good practice'..
45014     this.disable = this.disable || {};
45015     Roo.applyIf(this.disable, {
45016         fontSize : true,
45017         colors : true,
45018         specialElements : true
45019     });
45020     
45021     
45022     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45023     // dont call parent... till later.
45024 }
45025
45026 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45027     
45028     tb: false,
45029     
45030     rendered: false,
45031     
45032     editor : false,
45033     editorcore : false,
45034     /**
45035      * @cfg {Object} disable  List of toolbar elements to disable
45036          
45037      */
45038     disable : false,
45039     
45040     
45041      /**
45042      * @cfg {String} createLinkText The default text for the create link prompt
45043      */
45044     createLinkText : 'Please enter the URL for the link:',
45045     /**
45046      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45047      */
45048     defaultLinkValue : 'http:/'+'/',
45049    
45050     
45051       /**
45052      * @cfg {Array} fontFamilies An array of available font families
45053      */
45054     fontFamilies : [
45055         'Arial',
45056         'Courier New',
45057         'Tahoma',
45058         'Times New Roman',
45059         'Verdana'
45060     ],
45061     
45062     specialChars : [
45063            "&#169;",
45064           "&#174;",     
45065           "&#8482;",    
45066           "&#163;" ,    
45067          // "&#8212;",    
45068           "&#8230;",    
45069           "&#247;" ,    
45070         //  "&#225;" ,     ?? a acute?
45071            "&#8364;"    , //Euro
45072        //   "&#8220;"    ,
45073         //  "&#8221;"    ,
45074         //  "&#8226;"    ,
45075           "&#176;"  //   , // degrees
45076
45077          // "&#233;"     , // e ecute
45078          // "&#250;"     , // u ecute?
45079     ],
45080     
45081     specialElements : [
45082         {
45083             text: "Insert Table",
45084             xtype: 'MenuItem',
45085             xns : Roo.Menu,
45086             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45087                 
45088         },
45089         {    
45090             text: "Insert Image",
45091             xtype: 'MenuItem',
45092             xns : Roo.Menu,
45093             ihtml : '<img src="about:blank"/>'
45094             
45095         }
45096         
45097          
45098     ],
45099     
45100     
45101     inputElements : [ 
45102             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45103             "input:submit", "input:button", "select", "textarea", "label" ],
45104     formats : [
45105         ["p"] ,  
45106         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45107         ["pre"],[ "code"], 
45108         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45109         ['div'],['span']
45110     ],
45111     
45112     cleanStyles : [
45113         "font-size"
45114     ],
45115      /**
45116      * @cfg {String} defaultFont default font to use.
45117      */
45118     defaultFont: 'tahoma',
45119    
45120     fontSelect : false,
45121     
45122     
45123     formatCombo : false,
45124     
45125     init : function(editor)
45126     {
45127         this.editor = editor;
45128         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45129         var editorcore = this.editorcore;
45130         
45131         var _t = this;
45132         
45133         var fid = editorcore.frameId;
45134         var etb = this;
45135         function btn(id, toggle, handler){
45136             var xid = fid + '-'+ id ;
45137             return {
45138                 id : xid,
45139                 cmd : id,
45140                 cls : 'x-btn-icon x-edit-'+id,
45141                 enableToggle:toggle !== false,
45142                 scope: _t, // was editor...
45143                 handler:handler||_t.relayBtnCmd,
45144                 clickEvent:'mousedown',
45145                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45146                 tabIndex:-1
45147             };
45148         }
45149         
45150         
45151         
45152         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45153         this.tb = tb;
45154          // stop form submits
45155         tb.el.on('click', function(e){
45156             e.preventDefault(); // what does this do?
45157         });
45158
45159         if(!this.disable.font) { // && !Roo.isSafari){
45160             /* why no safari for fonts 
45161             editor.fontSelect = tb.el.createChild({
45162                 tag:'select',
45163                 tabIndex: -1,
45164                 cls:'x-font-select',
45165                 html: this.createFontOptions()
45166             });
45167             
45168             editor.fontSelect.on('change', function(){
45169                 var font = editor.fontSelect.dom.value;
45170                 editor.relayCmd('fontname', font);
45171                 editor.deferFocus();
45172             }, editor);
45173             
45174             tb.add(
45175                 editor.fontSelect.dom,
45176                 '-'
45177             );
45178             */
45179             
45180         };
45181         if(!this.disable.formats){
45182             this.formatCombo = new Roo.form.ComboBox({
45183                 store: new Roo.data.SimpleStore({
45184                     id : 'tag',
45185                     fields: ['tag'],
45186                     data : this.formats // from states.js
45187                 }),
45188                 blockFocus : true,
45189                 name : '',
45190                 //autoCreate : {tag: "div",  size: "20"},
45191                 displayField:'tag',
45192                 typeAhead: false,
45193                 mode: 'local',
45194                 editable : false,
45195                 triggerAction: 'all',
45196                 emptyText:'Add tag',
45197                 selectOnFocus:true,
45198                 width:135,
45199                 listeners : {
45200                     'select': function(c, r, i) {
45201                         editorcore.insertTag(r.get('tag'));
45202                         editor.focus();
45203                     }
45204                 }
45205
45206             });
45207             tb.addField(this.formatCombo);
45208             
45209         }
45210         
45211         if(!this.disable.format){
45212             tb.add(
45213                 btn('bold'),
45214                 btn('italic'),
45215                 btn('underline'),
45216                 btn('strikethrough')
45217             );
45218         };
45219         if(!this.disable.fontSize){
45220             tb.add(
45221                 '-',
45222                 
45223                 
45224                 btn('increasefontsize', false, editorcore.adjustFont),
45225                 btn('decreasefontsize', false, editorcore.adjustFont)
45226             );
45227         };
45228         
45229         
45230         if(!this.disable.colors){
45231             tb.add(
45232                 '-', {
45233                     id:editorcore.frameId +'-forecolor',
45234                     cls:'x-btn-icon x-edit-forecolor',
45235                     clickEvent:'mousedown',
45236                     tooltip: this.buttonTips['forecolor'] || undefined,
45237                     tabIndex:-1,
45238                     menu : new Roo.menu.ColorMenu({
45239                         allowReselect: true,
45240                         focus: Roo.emptyFn,
45241                         value:'000000',
45242                         plain:true,
45243                         selectHandler: function(cp, color){
45244                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45245                             editor.deferFocus();
45246                         },
45247                         scope: editorcore,
45248                         clickEvent:'mousedown'
45249                     })
45250                 }, {
45251                     id:editorcore.frameId +'backcolor',
45252                     cls:'x-btn-icon x-edit-backcolor',
45253                     clickEvent:'mousedown',
45254                     tooltip: this.buttonTips['backcolor'] || undefined,
45255                     tabIndex:-1,
45256                     menu : new Roo.menu.ColorMenu({
45257                         focus: Roo.emptyFn,
45258                         value:'FFFFFF',
45259                         plain:true,
45260                         allowReselect: true,
45261                         selectHandler: function(cp, color){
45262                             if(Roo.isGecko){
45263                                 editorcore.execCmd('useCSS', false);
45264                                 editorcore.execCmd('hilitecolor', color);
45265                                 editorcore.execCmd('useCSS', true);
45266                                 editor.deferFocus();
45267                             }else{
45268                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45269                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45270                                 editor.deferFocus();
45271                             }
45272                         },
45273                         scope:editorcore,
45274                         clickEvent:'mousedown'
45275                     })
45276                 }
45277             );
45278         };
45279         // now add all the items...
45280         
45281
45282         if(!this.disable.alignments){
45283             tb.add(
45284                 '-',
45285                 btn('justifyleft'),
45286                 btn('justifycenter'),
45287                 btn('justifyright')
45288             );
45289         };
45290
45291         //if(!Roo.isSafari){
45292             if(!this.disable.links){
45293                 tb.add(
45294                     '-',
45295                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45296                 );
45297             };
45298
45299             if(!this.disable.lists){
45300                 tb.add(
45301                     '-',
45302                     btn('insertorderedlist'),
45303                     btn('insertunorderedlist')
45304                 );
45305             }
45306             if(!this.disable.sourceEdit){
45307                 tb.add(
45308                     '-',
45309                     btn('sourceedit', true, function(btn){
45310                         this.toggleSourceEdit(btn.pressed);
45311                     })
45312                 );
45313             }
45314         //}
45315         
45316         var smenu = { };
45317         // special menu.. - needs to be tidied up..
45318         if (!this.disable.special) {
45319             smenu = {
45320                 text: "&#169;",
45321                 cls: 'x-edit-none',
45322                 
45323                 menu : {
45324                     items : []
45325                 }
45326             };
45327             for (var i =0; i < this.specialChars.length; i++) {
45328                 smenu.menu.items.push({
45329                     
45330                     html: this.specialChars[i],
45331                     handler: function(a,b) {
45332                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45333                         //editor.insertAtCursor(a.html);
45334                         
45335                     },
45336                     tabIndex:-1
45337                 });
45338             }
45339             
45340             
45341             tb.add(smenu);
45342             
45343             
45344         }
45345         
45346         var cmenu = { };
45347         if (!this.disable.cleanStyles) {
45348             cmenu = {
45349                 cls: 'x-btn-icon x-btn-clear',
45350                 
45351                 menu : {
45352                     items : []
45353                 }
45354             };
45355             for (var i =0; i < this.cleanStyles.length; i++) {
45356                 cmenu.menu.items.push({
45357                     actiontype : this.cleanStyles[i],
45358                     html: 'Remove ' + this.cleanStyles[i],
45359                     handler: function(a,b) {
45360 //                        Roo.log(a);
45361 //                        Roo.log(b);
45362                         var c = Roo.get(editorcore.doc.body);
45363                         c.select('[style]').each(function(s) {
45364                             s.dom.style.removeProperty(a.actiontype);
45365                         });
45366                         editorcore.syncValue();
45367                     },
45368                     tabIndex:-1
45369                 });
45370             }
45371              cmenu.menu.items.push({
45372                 actiontype : 'tablewidths',
45373                 html: 'Remove Table Widths',
45374                 handler: function(a,b) {
45375                     editorcore.cleanTableWidths();
45376                     editorcore.syncValue();
45377                 },
45378                 tabIndex:-1
45379             });
45380             cmenu.menu.items.push({
45381                 actiontype : 'word',
45382                 html: 'Remove MS Word Formating',
45383                 handler: function(a,b) {
45384                     editorcore.cleanWord();
45385                     editorcore.syncValue();
45386                 },
45387                 tabIndex:-1
45388             });
45389             
45390             cmenu.menu.items.push({
45391                 actiontype : 'all',
45392                 html: 'Remove All Styles',
45393                 handler: function(a,b) {
45394                     
45395                     var c = Roo.get(editorcore.doc.body);
45396                     c.select('[style]').each(function(s) {
45397                         s.dom.removeAttribute('style');
45398                     });
45399                     editorcore.syncValue();
45400                 },
45401                 tabIndex:-1
45402             });
45403             
45404             cmenu.menu.items.push({
45405                 actiontype : 'all',
45406                 html: 'Remove All CSS Classes',
45407                 handler: function(a,b) {
45408                     
45409                     var c = Roo.get(editorcore.doc.body);
45410                     c.select('[class]').each(function(s) {
45411                         s.dom.className = '';
45412                     });
45413                     editorcore.syncValue();
45414                 },
45415                 tabIndex:-1
45416             });
45417             
45418              cmenu.menu.items.push({
45419                 actiontype : 'tidy',
45420                 html: 'Tidy HTML Source',
45421                 handler: function(a,b) {
45422                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45423                     editorcore.syncValue();
45424                 },
45425                 tabIndex:-1
45426             });
45427             
45428             
45429             tb.add(cmenu);
45430         }
45431          
45432         if (!this.disable.specialElements) {
45433             var semenu = {
45434                 text: "Other;",
45435                 cls: 'x-edit-none',
45436                 menu : {
45437                     items : []
45438                 }
45439             };
45440             for (var i =0; i < this.specialElements.length; i++) {
45441                 semenu.menu.items.push(
45442                     Roo.apply({ 
45443                         handler: function(a,b) {
45444                             editor.insertAtCursor(this.ihtml);
45445                         }
45446                     }, this.specialElements[i])
45447                 );
45448                     
45449             }
45450             
45451             tb.add(semenu);
45452             
45453             
45454         }
45455          
45456         
45457         if (this.btns) {
45458             for(var i =0; i< this.btns.length;i++) {
45459                 var b = Roo.factory(this.btns[i],Roo.form);
45460                 b.cls =  'x-edit-none';
45461                 
45462                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45463                     b.cls += ' x-init-enable';
45464                 }
45465                 
45466                 b.scope = editorcore;
45467                 tb.add(b);
45468             }
45469         
45470         }
45471         
45472         
45473         
45474         // disable everything...
45475         
45476         this.tb.items.each(function(item){
45477             
45478            if(
45479                 item.id != editorcore.frameId+ '-sourceedit' && 
45480                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45481             ){
45482                 
45483                 item.disable();
45484             }
45485         });
45486         this.rendered = true;
45487         
45488         // the all the btns;
45489         editor.on('editorevent', this.updateToolbar, this);
45490         // other toolbars need to implement this..
45491         //editor.on('editmodechange', this.updateToolbar, this);
45492     },
45493     
45494     
45495     relayBtnCmd : function(btn) {
45496         this.editorcore.relayCmd(btn.cmd);
45497     },
45498     // private used internally
45499     createLink : function(){
45500         Roo.log("create link?");
45501         var url = prompt(this.createLinkText, this.defaultLinkValue);
45502         if(url && url != 'http:/'+'/'){
45503             this.editorcore.relayCmd('createlink', url);
45504         }
45505     },
45506
45507     
45508     /**
45509      * Protected method that will not generally be called directly. It triggers
45510      * a toolbar update by reading the markup state of the current selection in the editor.
45511      */
45512     updateToolbar: function(){
45513
45514         if(!this.editorcore.activated){
45515             this.editor.onFirstFocus();
45516             return;
45517         }
45518
45519         var btns = this.tb.items.map, 
45520             doc = this.editorcore.doc,
45521             frameId = this.editorcore.frameId;
45522
45523         if(!this.disable.font && !Roo.isSafari){
45524             /*
45525             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45526             if(name != this.fontSelect.dom.value){
45527                 this.fontSelect.dom.value = name;
45528             }
45529             */
45530         }
45531         if(!this.disable.format){
45532             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45533             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45534             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45535             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45536         }
45537         if(!this.disable.alignments){
45538             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45539             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45540             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45541         }
45542         if(!Roo.isSafari && !this.disable.lists){
45543             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45544             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45545         }
45546         
45547         var ans = this.editorcore.getAllAncestors();
45548         if (this.formatCombo) {
45549             
45550             
45551             var store = this.formatCombo.store;
45552             this.formatCombo.setValue("");
45553             for (var i =0; i < ans.length;i++) {
45554                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45555                     // select it..
45556                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45557                     break;
45558                 }
45559             }
45560         }
45561         
45562         
45563         
45564         // hides menus... - so this cant be on a menu...
45565         Roo.menu.MenuMgr.hideAll();
45566
45567         //this.editorsyncValue();
45568     },
45569    
45570     
45571     createFontOptions : function(){
45572         var buf = [], fs = this.fontFamilies, ff, lc;
45573         
45574         
45575         
45576         for(var i = 0, len = fs.length; i< len; i++){
45577             ff = fs[i];
45578             lc = ff.toLowerCase();
45579             buf.push(
45580                 '<option value="',lc,'" style="font-family:',ff,';"',
45581                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45582                     ff,
45583                 '</option>'
45584             );
45585         }
45586         return buf.join('');
45587     },
45588     
45589     toggleSourceEdit : function(sourceEditMode){
45590         
45591         Roo.log("toolbar toogle");
45592         if(sourceEditMode === undefined){
45593             sourceEditMode = !this.sourceEditMode;
45594         }
45595         this.sourceEditMode = sourceEditMode === true;
45596         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45597         // just toggle the button?
45598         if(btn.pressed !== this.sourceEditMode){
45599             btn.toggle(this.sourceEditMode);
45600             return;
45601         }
45602         
45603         if(sourceEditMode){
45604             Roo.log("disabling buttons");
45605             this.tb.items.each(function(item){
45606                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45607                     item.disable();
45608                 }
45609             });
45610           
45611         }else{
45612             Roo.log("enabling buttons");
45613             if(this.editorcore.initialized){
45614                 this.tb.items.each(function(item){
45615                     item.enable();
45616                 });
45617             }
45618             
45619         }
45620         Roo.log("calling toggole on editor");
45621         // tell the editor that it's been pressed..
45622         this.editor.toggleSourceEdit(sourceEditMode);
45623        
45624     },
45625      /**
45626      * Object collection of toolbar tooltips for the buttons in the editor. The key
45627      * is the command id associated with that button and the value is a valid QuickTips object.
45628      * For example:
45629 <pre><code>
45630 {
45631     bold : {
45632         title: 'Bold (Ctrl+B)',
45633         text: 'Make the selected text bold.',
45634         cls: 'x-html-editor-tip'
45635     },
45636     italic : {
45637         title: 'Italic (Ctrl+I)',
45638         text: 'Make the selected text italic.',
45639         cls: 'x-html-editor-tip'
45640     },
45641     ...
45642 </code></pre>
45643     * @type Object
45644      */
45645     buttonTips : {
45646         bold : {
45647             title: 'Bold (Ctrl+B)',
45648             text: 'Make the selected text bold.',
45649             cls: 'x-html-editor-tip'
45650         },
45651         italic : {
45652             title: 'Italic (Ctrl+I)',
45653             text: 'Make the selected text italic.',
45654             cls: 'x-html-editor-tip'
45655         },
45656         underline : {
45657             title: 'Underline (Ctrl+U)',
45658             text: 'Underline the selected text.',
45659             cls: 'x-html-editor-tip'
45660         },
45661         strikethrough : {
45662             title: 'Strikethrough',
45663             text: 'Strikethrough the selected text.',
45664             cls: 'x-html-editor-tip'
45665         },
45666         increasefontsize : {
45667             title: 'Grow Text',
45668             text: 'Increase the font size.',
45669             cls: 'x-html-editor-tip'
45670         },
45671         decreasefontsize : {
45672             title: 'Shrink Text',
45673             text: 'Decrease the font size.',
45674             cls: 'x-html-editor-tip'
45675         },
45676         backcolor : {
45677             title: 'Text Highlight Color',
45678             text: 'Change the background color of the selected text.',
45679             cls: 'x-html-editor-tip'
45680         },
45681         forecolor : {
45682             title: 'Font Color',
45683             text: 'Change the color of the selected text.',
45684             cls: 'x-html-editor-tip'
45685         },
45686         justifyleft : {
45687             title: 'Align Text Left',
45688             text: 'Align text to the left.',
45689             cls: 'x-html-editor-tip'
45690         },
45691         justifycenter : {
45692             title: 'Center Text',
45693             text: 'Center text in the editor.',
45694             cls: 'x-html-editor-tip'
45695         },
45696         justifyright : {
45697             title: 'Align Text Right',
45698             text: 'Align text to the right.',
45699             cls: 'x-html-editor-tip'
45700         },
45701         insertunorderedlist : {
45702             title: 'Bullet List',
45703             text: 'Start a bulleted list.',
45704             cls: 'x-html-editor-tip'
45705         },
45706         insertorderedlist : {
45707             title: 'Numbered List',
45708             text: 'Start a numbered list.',
45709             cls: 'x-html-editor-tip'
45710         },
45711         createlink : {
45712             title: 'Hyperlink',
45713             text: 'Make the selected text a hyperlink.',
45714             cls: 'x-html-editor-tip'
45715         },
45716         sourceedit : {
45717             title: 'Source Edit',
45718             text: 'Switch to source editing mode.',
45719             cls: 'x-html-editor-tip'
45720         }
45721     },
45722     // private
45723     onDestroy : function(){
45724         if(this.rendered){
45725             
45726             this.tb.items.each(function(item){
45727                 if(item.menu){
45728                     item.menu.removeAll();
45729                     if(item.menu.el){
45730                         item.menu.el.destroy();
45731                     }
45732                 }
45733                 item.destroy();
45734             });
45735              
45736         }
45737     },
45738     onFirstFocus: function() {
45739         this.tb.items.each(function(item){
45740            item.enable();
45741         });
45742     }
45743 });
45744
45745
45746
45747
45748 // <script type="text/javascript">
45749 /*
45750  * Based on
45751  * Ext JS Library 1.1.1
45752  * Copyright(c) 2006-2007, Ext JS, LLC.
45753  *  
45754  
45755  */
45756
45757  
45758 /**
45759  * @class Roo.form.HtmlEditor.ToolbarContext
45760  * Context Toolbar
45761  * 
45762  * Usage:
45763  *
45764  new Roo.form.HtmlEditor({
45765     ....
45766     toolbars : [
45767         { xtype: 'ToolbarStandard', styles : {} }
45768         { xtype: 'ToolbarContext', disable : {} }
45769     ]
45770 })
45771
45772      
45773  * 
45774  * @config : {Object} disable List of elements to disable.. (not done yet.)
45775  * @config : {Object} styles  Map of styles available.
45776  * 
45777  */
45778
45779 Roo.form.HtmlEditor.ToolbarContext = function(config)
45780 {
45781     
45782     Roo.apply(this, config);
45783     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45784     // dont call parent... till later.
45785     this.styles = this.styles || {};
45786 }
45787
45788  
45789
45790 Roo.form.HtmlEditor.ToolbarContext.types = {
45791     'IMG' : {
45792         width : {
45793             title: "Width",
45794             width: 40
45795         },
45796         height:  {
45797             title: "Height",
45798             width: 40
45799         },
45800         align: {
45801             title: "Align",
45802             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45803             width : 80
45804             
45805         },
45806         border: {
45807             title: "Border",
45808             width: 40
45809         },
45810         alt: {
45811             title: "Alt",
45812             width: 120
45813         },
45814         src : {
45815             title: "Src",
45816             width: 220
45817         }
45818         
45819     },
45820     'A' : {
45821         name : {
45822             title: "Name",
45823             width: 50
45824         },
45825         target:  {
45826             title: "Target",
45827             width: 120
45828         },
45829         href:  {
45830             title: "Href",
45831             width: 220
45832         } // border?
45833         
45834     },
45835     'TABLE' : {
45836         rows : {
45837             title: "Rows",
45838             width: 20
45839         },
45840         cols : {
45841             title: "Cols",
45842             width: 20
45843         },
45844         width : {
45845             title: "Width",
45846             width: 40
45847         },
45848         height : {
45849             title: "Height",
45850             width: 40
45851         },
45852         border : {
45853             title: "Border",
45854             width: 20
45855         }
45856     },
45857     'TD' : {
45858         width : {
45859             title: "Width",
45860             width: 40
45861         },
45862         height : {
45863             title: "Height",
45864             width: 40
45865         },   
45866         align: {
45867             title: "Align",
45868             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
45869             width: 80
45870         },
45871         valign: {
45872             title: "Valign",
45873             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
45874             width: 80
45875         },
45876         colspan: {
45877             title: "Colspan",
45878             width: 20
45879             
45880         },
45881          'font-family'  : {
45882             title : "Font",
45883             style : 'fontFamily',
45884             displayField: 'display',
45885             optname : 'font-family',
45886             width: 140
45887         }
45888     },
45889     'INPUT' : {
45890         name : {
45891             title: "name",
45892             width: 120
45893         },
45894         value : {
45895             title: "Value",
45896             width: 120
45897         },
45898         width : {
45899             title: "Width",
45900             width: 40
45901         }
45902     },
45903     'LABEL' : {
45904         'for' : {
45905             title: "For",
45906             width: 120
45907         }
45908     },
45909     'TEXTAREA' : {
45910           name : {
45911             title: "name",
45912             width: 120
45913         },
45914         rows : {
45915             title: "Rows",
45916             width: 20
45917         },
45918         cols : {
45919             title: "Cols",
45920             width: 20
45921         }
45922     },
45923     'SELECT' : {
45924         name : {
45925             title: "name",
45926             width: 120
45927         },
45928         selectoptions : {
45929             title: "Options",
45930             width: 200
45931         }
45932     },
45933     
45934     // should we really allow this??
45935     // should this just be 
45936     'BODY' : {
45937         title : {
45938             title: "Title",
45939             width: 200,
45940             disabled : true
45941         }
45942     },
45943     'SPAN' : {
45944         'font-family'  : {
45945             title : "Font",
45946             style : 'fontFamily',
45947             displayField: 'display',
45948             optname : 'font-family',
45949             width: 140
45950         }
45951     },
45952     'DIV' : {
45953         'font-family'  : {
45954             title : "Font",
45955             style : 'fontFamily',
45956             displayField: 'display',
45957             optname : 'font-family',
45958             width: 140
45959         }
45960     },
45961      'P' : {
45962         'font-family'  : {
45963             title : "Font",
45964             style : 'fontFamily',
45965             displayField: 'display',
45966             optname : 'font-family',
45967             width: 140
45968         }
45969     },
45970     
45971     '*' : {
45972         // empty..
45973     }
45974
45975 };
45976
45977 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
45978 Roo.form.HtmlEditor.ToolbarContext.stores = false;
45979
45980 Roo.form.HtmlEditor.ToolbarContext.options = {
45981         'font-family'  : [ 
45982                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
45983                 [ 'Courier New', 'Courier New'],
45984                 [ 'Tahoma', 'Tahoma'],
45985                 [ 'Times New Roman,serif', 'Times'],
45986                 [ 'Verdana','Verdana' ]
45987         ]
45988 };
45989
45990 // fixme - these need to be configurable..
45991  
45992
45993 //Roo.form.HtmlEditor.ToolbarContext.types
45994
45995
45996 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
45997     
45998     tb: false,
45999     
46000     rendered: false,
46001     
46002     editor : false,
46003     editorcore : false,
46004     /**
46005      * @cfg {Object} disable  List of toolbar elements to disable
46006          
46007      */
46008     disable : false,
46009     /**
46010      * @cfg {Object} styles List of styles 
46011      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46012      *
46013      * These must be defined in the page, so they get rendered correctly..
46014      * .headline { }
46015      * TD.underline { }
46016      * 
46017      */
46018     styles : false,
46019     
46020     options: false,
46021     
46022     toolbars : false,
46023     
46024     init : function(editor)
46025     {
46026         this.editor = editor;
46027         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46028         var editorcore = this.editorcore;
46029         
46030         var fid = editorcore.frameId;
46031         var etb = this;
46032         function btn(id, toggle, handler){
46033             var xid = fid + '-'+ id ;
46034             return {
46035                 id : xid,
46036                 cmd : id,
46037                 cls : 'x-btn-icon x-edit-'+id,
46038                 enableToggle:toggle !== false,
46039                 scope: editorcore, // was editor...
46040                 handler:handler||editorcore.relayBtnCmd,
46041                 clickEvent:'mousedown',
46042                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46043                 tabIndex:-1
46044             };
46045         }
46046         // create a new element.
46047         var wdiv = editor.wrap.createChild({
46048                 tag: 'div'
46049             }, editor.wrap.dom.firstChild.nextSibling, true);
46050         
46051         // can we do this more than once??
46052         
46053          // stop form submits
46054       
46055  
46056         // disable everything...
46057         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46058         this.toolbars = {};
46059            
46060         for (var i in  ty) {
46061           
46062             this.toolbars[i] = this.buildToolbar(ty[i],i);
46063         }
46064         this.tb = this.toolbars.BODY;
46065         this.tb.el.show();
46066         this.buildFooter();
46067         this.footer.show();
46068         editor.on('hide', function( ) { this.footer.hide() }, this);
46069         editor.on('show', function( ) { this.footer.show() }, this);
46070         
46071          
46072         this.rendered = true;
46073         
46074         // the all the btns;
46075         editor.on('editorevent', this.updateToolbar, this);
46076         // other toolbars need to implement this..
46077         //editor.on('editmodechange', this.updateToolbar, this);
46078     },
46079     
46080     
46081     
46082     /**
46083      * Protected method that will not generally be called directly. It triggers
46084      * a toolbar update by reading the markup state of the current selection in the editor.
46085      *
46086      * Note you can force an update by calling on('editorevent', scope, false)
46087      */
46088     updateToolbar: function(editor,ev,sel){
46089
46090         //Roo.log(ev);
46091         // capture mouse up - this is handy for selecting images..
46092         // perhaps should go somewhere else...
46093         if(!this.editorcore.activated){
46094              this.editor.onFirstFocus();
46095             return;
46096         }
46097         
46098         
46099         
46100         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46101         // selectNode - might want to handle IE?
46102         if (ev &&
46103             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46104             ev.target && ev.target.tagName == 'IMG') {
46105             // they have click on an image...
46106             // let's see if we can change the selection...
46107             sel = ev.target;
46108          
46109               var nodeRange = sel.ownerDocument.createRange();
46110             try {
46111                 nodeRange.selectNode(sel);
46112             } catch (e) {
46113                 nodeRange.selectNodeContents(sel);
46114             }
46115             //nodeRange.collapse(true);
46116             var s = this.editorcore.win.getSelection();
46117             s.removeAllRanges();
46118             s.addRange(nodeRange);
46119         }  
46120         
46121       
46122         var updateFooter = sel ? false : true;
46123         
46124         
46125         var ans = this.editorcore.getAllAncestors();
46126         
46127         // pick
46128         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46129         
46130         if (!sel) { 
46131             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46132             sel = sel ? sel : this.editorcore.doc.body;
46133             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46134             
46135         }
46136         // pick a menu that exists..
46137         var tn = sel.tagName.toUpperCase();
46138         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46139         
46140         tn = sel.tagName.toUpperCase();
46141         
46142         var lastSel = this.tb.selectedNode;
46143         
46144         this.tb.selectedNode = sel;
46145         
46146         // if current menu does not match..
46147         
46148         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46149                 
46150             this.tb.el.hide();
46151             ///console.log("show: " + tn);
46152             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46153             this.tb.el.show();
46154             // update name
46155             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46156             
46157             
46158             // update attributes
46159             if (this.tb.fields) {
46160                 this.tb.fields.each(function(e) {
46161                     if (e.stylename) {
46162                         e.setValue(sel.style[e.stylename]);
46163                         return;
46164                     } 
46165                    e.setValue(sel.getAttribute(e.attrname));
46166                 });
46167             }
46168             
46169             var hasStyles = false;
46170             for(var i in this.styles) {
46171                 hasStyles = true;
46172                 break;
46173             }
46174             
46175             // update styles
46176             if (hasStyles) { 
46177                 var st = this.tb.fields.item(0);
46178                 
46179                 st.store.removeAll();
46180                
46181                 
46182                 var cn = sel.className.split(/\s+/);
46183                 
46184                 var avs = [];
46185                 if (this.styles['*']) {
46186                     
46187                     Roo.each(this.styles['*'], function(v) {
46188                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46189                     });
46190                 }
46191                 if (this.styles[tn]) { 
46192                     Roo.each(this.styles[tn], function(v) {
46193                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46194                     });
46195                 }
46196                 
46197                 st.store.loadData(avs);
46198                 st.collapse();
46199                 st.setValue(cn);
46200             }
46201             // flag our selected Node.
46202             this.tb.selectedNode = sel;
46203            
46204            
46205             Roo.menu.MenuMgr.hideAll();
46206
46207         }
46208         
46209         if (!updateFooter) {
46210             //this.footDisp.dom.innerHTML = ''; 
46211             return;
46212         }
46213         // update the footer
46214         //
46215         var html = '';
46216         
46217         this.footerEls = ans.reverse();
46218         Roo.each(this.footerEls, function(a,i) {
46219             if (!a) { return; }
46220             html += html.length ? ' &gt; '  :  '';
46221             
46222             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46223             
46224         });
46225        
46226         // 
46227         var sz = this.footDisp.up('td').getSize();
46228         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46229         this.footDisp.dom.style.marginLeft = '5px';
46230         
46231         this.footDisp.dom.style.overflow = 'hidden';
46232         
46233         this.footDisp.dom.innerHTML = html;
46234             
46235         //this.editorsyncValue();
46236     },
46237      
46238     
46239    
46240        
46241     // private
46242     onDestroy : function(){
46243         if(this.rendered){
46244             
46245             this.tb.items.each(function(item){
46246                 if(item.menu){
46247                     item.menu.removeAll();
46248                     if(item.menu.el){
46249                         item.menu.el.destroy();
46250                     }
46251                 }
46252                 item.destroy();
46253             });
46254              
46255         }
46256     },
46257     onFirstFocus: function() {
46258         // need to do this for all the toolbars..
46259         this.tb.items.each(function(item){
46260            item.enable();
46261         });
46262     },
46263     buildToolbar: function(tlist, nm)
46264     {
46265         var editor = this.editor;
46266         var editorcore = this.editorcore;
46267          // create a new element.
46268         var wdiv = editor.wrap.createChild({
46269                 tag: 'div'
46270             }, editor.wrap.dom.firstChild.nextSibling, true);
46271         
46272        
46273         var tb = new Roo.Toolbar(wdiv);
46274         // add the name..
46275         
46276         tb.add(nm+ ":&nbsp;");
46277         
46278         var styles = [];
46279         for(var i in this.styles) {
46280             styles.push(i);
46281         }
46282         
46283         // styles...
46284         if (styles && styles.length) {
46285             
46286             // this needs a multi-select checkbox...
46287             tb.addField( new Roo.form.ComboBox({
46288                 store: new Roo.data.SimpleStore({
46289                     id : 'val',
46290                     fields: ['val', 'selected'],
46291                     data : [] 
46292                 }),
46293                 name : '-roo-edit-className',
46294                 attrname : 'className',
46295                 displayField: 'val',
46296                 typeAhead: false,
46297                 mode: 'local',
46298                 editable : false,
46299                 triggerAction: 'all',
46300                 emptyText:'Select Style',
46301                 selectOnFocus:true,
46302                 width: 130,
46303                 listeners : {
46304                     'select': function(c, r, i) {
46305                         // initial support only for on class per el..
46306                         tb.selectedNode.className =  r ? r.get('val') : '';
46307                         editorcore.syncValue();
46308                     }
46309                 }
46310     
46311             }));
46312         }
46313         
46314         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46315         var tbops = tbc.options;
46316         
46317         for (var i in tlist) {
46318             
46319             var item = tlist[i];
46320             tb.add(item.title + ":&nbsp;");
46321             
46322             
46323             //optname == used so you can configure the options available..
46324             var opts = item.opts ? item.opts : false;
46325             if (item.optname) {
46326                 opts = tbops[item.optname];
46327            
46328             }
46329             
46330             if (opts) {
46331                 // opts == pulldown..
46332                 tb.addField( new Roo.form.ComboBox({
46333                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46334                         id : 'val',
46335                         fields: ['val', 'display'],
46336                         data : opts  
46337                     }),
46338                     name : '-roo-edit-' + i,
46339                     attrname : i,
46340                     stylename : item.style ? item.style : false,
46341                     displayField: item.displayField ? item.displayField : 'val',
46342                     valueField :  'val',
46343                     typeAhead: false,
46344                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46345                     editable : false,
46346                     triggerAction: 'all',
46347                     emptyText:'Select',
46348                     selectOnFocus:true,
46349                     width: item.width ? item.width  : 130,
46350                     listeners : {
46351                         'select': function(c, r, i) {
46352                             if (c.stylename) {
46353                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46354                                 return;
46355                             }
46356                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46357                         }
46358                     }
46359
46360                 }));
46361                 continue;
46362                     
46363                  
46364                 
46365                 tb.addField( new Roo.form.TextField({
46366                     name: i,
46367                     width: 100,
46368                     //allowBlank:false,
46369                     value: ''
46370                 }));
46371                 continue;
46372             }
46373             tb.addField( new Roo.form.TextField({
46374                 name: '-roo-edit-' + i,
46375                 attrname : i,
46376                 
46377                 width: item.width,
46378                 //allowBlank:true,
46379                 value: '',
46380                 listeners: {
46381                     'change' : function(f, nv, ov) {
46382                         tb.selectedNode.setAttribute(f.attrname, nv);
46383                         editorcore.syncValue();
46384                     }
46385                 }
46386             }));
46387              
46388         }
46389         
46390         var _this = this;
46391         
46392         if(nm == 'BODY'){
46393             tb.addSeparator();
46394         
46395             tb.addButton( {
46396                 text: 'Stylesheets',
46397
46398                 listeners : {
46399                     click : function ()
46400                     {
46401                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46402                     }
46403                 }
46404             });
46405         }
46406         
46407         tb.addFill();
46408         tb.addButton( {
46409             text: 'Remove Tag',
46410     
46411             listeners : {
46412                 click : function ()
46413                 {
46414                     // remove
46415                     // undo does not work.
46416                      
46417                     var sn = tb.selectedNode;
46418                     
46419                     var pn = sn.parentNode;
46420                     
46421                     var stn =  sn.childNodes[0];
46422                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46423                     while (sn.childNodes.length) {
46424                         var node = sn.childNodes[0];
46425                         sn.removeChild(node);
46426                         //Roo.log(node);
46427                         pn.insertBefore(node, sn);
46428                         
46429                     }
46430                     pn.removeChild(sn);
46431                     var range = editorcore.createRange();
46432         
46433                     range.setStart(stn,0);
46434                     range.setEnd(en,0); //????
46435                     //range.selectNode(sel);
46436                     
46437                     
46438                     var selection = editorcore.getSelection();
46439                     selection.removeAllRanges();
46440                     selection.addRange(range);
46441                     
46442                     
46443                     
46444                     //_this.updateToolbar(null, null, pn);
46445                     _this.updateToolbar(null, null, null);
46446                     _this.footDisp.dom.innerHTML = ''; 
46447                 }
46448             }
46449             
46450                     
46451                 
46452             
46453         });
46454         
46455         
46456         tb.el.on('click', function(e){
46457             e.preventDefault(); // what does this do?
46458         });
46459         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46460         tb.el.hide();
46461         tb.name = nm;
46462         // dont need to disable them... as they will get hidden
46463         return tb;
46464          
46465         
46466     },
46467     buildFooter : function()
46468     {
46469         
46470         var fel = this.editor.wrap.createChild();
46471         this.footer = new Roo.Toolbar(fel);
46472         // toolbar has scrolly on left / right?
46473         var footDisp= new Roo.Toolbar.Fill();
46474         var _t = this;
46475         this.footer.add(
46476             {
46477                 text : '&lt;',
46478                 xtype: 'Button',
46479                 handler : function() {
46480                     _t.footDisp.scrollTo('left',0,true)
46481                 }
46482             }
46483         );
46484         this.footer.add( footDisp );
46485         this.footer.add( 
46486             {
46487                 text : '&gt;',
46488                 xtype: 'Button',
46489                 handler : function() {
46490                     // no animation..
46491                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46492                 }
46493             }
46494         );
46495         var fel = Roo.get(footDisp.el);
46496         fel.addClass('x-editor-context');
46497         this.footDispWrap = fel; 
46498         this.footDispWrap.overflow  = 'hidden';
46499         
46500         this.footDisp = fel.createChild();
46501         this.footDispWrap.on('click', this.onContextClick, this)
46502         
46503         
46504     },
46505     onContextClick : function (ev,dom)
46506     {
46507         ev.preventDefault();
46508         var  cn = dom.className;
46509         //Roo.log(cn);
46510         if (!cn.match(/x-ed-loc-/)) {
46511             return;
46512         }
46513         var n = cn.split('-').pop();
46514         var ans = this.footerEls;
46515         var sel = ans[n];
46516         
46517          // pick
46518         var range = this.editorcore.createRange();
46519         
46520         range.selectNodeContents(sel);
46521         //range.selectNode(sel);
46522         
46523         
46524         var selection = this.editorcore.getSelection();
46525         selection.removeAllRanges();
46526         selection.addRange(range);
46527         
46528         
46529         
46530         this.updateToolbar(null, null, sel);
46531         
46532         
46533     }
46534     
46535     
46536     
46537     
46538     
46539 });
46540
46541
46542
46543
46544
46545 /*
46546  * Based on:
46547  * Ext JS Library 1.1.1
46548  * Copyright(c) 2006-2007, Ext JS, LLC.
46549  *
46550  * Originally Released Under LGPL - original licence link has changed is not relivant.
46551  *
46552  * Fork - LGPL
46553  * <script type="text/javascript">
46554  */
46555  
46556 /**
46557  * @class Roo.form.BasicForm
46558  * @extends Roo.util.Observable
46559  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46560  * @constructor
46561  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46562  * @param {Object} config Configuration options
46563  */
46564 Roo.form.BasicForm = function(el, config){
46565     this.allItems = [];
46566     this.childForms = [];
46567     Roo.apply(this, config);
46568     /*
46569      * The Roo.form.Field items in this form.
46570      * @type MixedCollection
46571      */
46572      
46573      
46574     this.items = new Roo.util.MixedCollection(false, function(o){
46575         return o.id || (o.id = Roo.id());
46576     });
46577     this.addEvents({
46578         /**
46579          * @event beforeaction
46580          * Fires before any action is performed. Return false to cancel the action.
46581          * @param {Form} this
46582          * @param {Action} action The action to be performed
46583          */
46584         beforeaction: true,
46585         /**
46586          * @event actionfailed
46587          * Fires when an action fails.
46588          * @param {Form} this
46589          * @param {Action} action The action that failed
46590          */
46591         actionfailed : true,
46592         /**
46593          * @event actioncomplete
46594          * Fires when an action is completed.
46595          * @param {Form} this
46596          * @param {Action} action The action that completed
46597          */
46598         actioncomplete : true
46599     });
46600     if(el){
46601         this.initEl(el);
46602     }
46603     Roo.form.BasicForm.superclass.constructor.call(this);
46604 };
46605
46606 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46607     /**
46608      * @cfg {String} method
46609      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46610      */
46611     /**
46612      * @cfg {DataReader} reader
46613      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46614      * This is optional as there is built-in support for processing JSON.
46615      */
46616     /**
46617      * @cfg {DataReader} errorReader
46618      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46619      * This is completely optional as there is built-in support for processing JSON.
46620      */
46621     /**
46622      * @cfg {String} url
46623      * The URL to use for form actions if one isn't supplied in the action options.
46624      */
46625     /**
46626      * @cfg {Boolean} fileUpload
46627      * Set to true if this form is a file upload.
46628      */
46629      
46630     /**
46631      * @cfg {Object} baseParams
46632      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46633      */
46634      /**
46635      
46636     /**
46637      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46638      */
46639     timeout: 30,
46640
46641     // private
46642     activeAction : null,
46643
46644     /**
46645      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46646      * or setValues() data instead of when the form was first created.
46647      */
46648     trackResetOnLoad : false,
46649     
46650     
46651     /**
46652      * childForms - used for multi-tab forms
46653      * @type {Array}
46654      */
46655     childForms : false,
46656     
46657     /**
46658      * allItems - full list of fields.
46659      * @type {Array}
46660      */
46661     allItems : false,
46662     
46663     /**
46664      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46665      * element by passing it or its id or mask the form itself by passing in true.
46666      * @type Mixed
46667      */
46668     waitMsgTarget : false,
46669
46670     // private
46671     initEl : function(el){
46672         this.el = Roo.get(el);
46673         this.id = this.el.id || Roo.id();
46674         this.el.on('submit', this.onSubmit, this);
46675         this.el.addClass('x-form');
46676     },
46677
46678     // private
46679     onSubmit : function(e){
46680         e.stopEvent();
46681     },
46682
46683     /**
46684      * Returns true if client-side validation on the form is successful.
46685      * @return Boolean
46686      */
46687     isValid : function(){
46688         var valid = true;
46689         this.items.each(function(f){
46690            if(!f.validate()){
46691                valid = false;
46692            }
46693         });
46694         return valid;
46695     },
46696
46697     /**
46698      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46699      * @return Boolean
46700      */
46701     isDirty : function(){
46702         var dirty = false;
46703         this.items.each(function(f){
46704            if(f.isDirty()){
46705                dirty = true;
46706                return false;
46707            }
46708         });
46709         return dirty;
46710     },
46711     
46712     /**
46713      * Returns true if any fields in this form have changed since their original load. (New version)
46714      * @return Boolean
46715      */
46716     
46717     hasChanged : function()
46718     {
46719         var dirty = false;
46720         this.items.each(function(f){
46721            if(f.hasChanged()){
46722                dirty = true;
46723                return false;
46724            }
46725         });
46726         return dirty;
46727         
46728     },
46729     /**
46730      * Resets all hasChanged to 'false' -
46731      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46732      * So hasChanged storage is only to be used for this purpose
46733      * @return Boolean
46734      */
46735     resetHasChanged : function()
46736     {
46737         this.items.each(function(f){
46738            f.resetHasChanged();
46739         });
46740         
46741     },
46742     
46743     
46744     /**
46745      * Performs a predefined action (submit or load) or custom actions you define on this form.
46746      * @param {String} actionName The name of the action type
46747      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46748      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46749      * accept other config options):
46750      * <pre>
46751 Property          Type             Description
46752 ----------------  ---------------  ----------------------------------------------------------------------------------
46753 url               String           The url for the action (defaults to the form's url)
46754 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46755 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46756 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46757                                    validate the form on the client (defaults to false)
46758      * </pre>
46759      * @return {BasicForm} this
46760      */
46761     doAction : function(action, options){
46762         if(typeof action == 'string'){
46763             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46764         }
46765         if(this.fireEvent('beforeaction', this, action) !== false){
46766             this.beforeAction(action);
46767             action.run.defer(100, action);
46768         }
46769         return this;
46770     },
46771
46772     /**
46773      * Shortcut to do a submit action.
46774      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46775      * @return {BasicForm} this
46776      */
46777     submit : function(options){
46778         this.doAction('submit', options);
46779         return this;
46780     },
46781
46782     /**
46783      * Shortcut to do a load action.
46784      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46785      * @return {BasicForm} this
46786      */
46787     load : function(options){
46788         this.doAction('load', options);
46789         return this;
46790     },
46791
46792     /**
46793      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46794      * @param {Record} record The record to edit
46795      * @return {BasicForm} this
46796      */
46797     updateRecord : function(record){
46798         record.beginEdit();
46799         var fs = record.fields;
46800         fs.each(function(f){
46801             var field = this.findField(f.name);
46802             if(field){
46803                 record.set(f.name, field.getValue());
46804             }
46805         }, this);
46806         record.endEdit();
46807         return this;
46808     },
46809
46810     /**
46811      * Loads an Roo.data.Record into this form.
46812      * @param {Record} record The record to load
46813      * @return {BasicForm} this
46814      */
46815     loadRecord : function(record){
46816         this.setValues(record.data);
46817         return this;
46818     },
46819
46820     // private
46821     beforeAction : function(action){
46822         var o = action.options;
46823         
46824        
46825         if(this.waitMsgTarget === true){
46826             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46827         }else if(this.waitMsgTarget){
46828             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46829             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46830         }else {
46831             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46832         }
46833          
46834     },
46835
46836     // private
46837     afterAction : function(action, success){
46838         this.activeAction = null;
46839         var o = action.options;
46840         
46841         if(this.waitMsgTarget === true){
46842             this.el.unmask();
46843         }else if(this.waitMsgTarget){
46844             this.waitMsgTarget.unmask();
46845         }else{
46846             Roo.MessageBox.updateProgress(1);
46847             Roo.MessageBox.hide();
46848         }
46849          
46850         if(success){
46851             if(o.reset){
46852                 this.reset();
46853             }
46854             Roo.callback(o.success, o.scope, [this, action]);
46855             this.fireEvent('actioncomplete', this, action);
46856             
46857         }else{
46858             
46859             // failure condition..
46860             // we have a scenario where updates need confirming.
46861             // eg. if a locking scenario exists..
46862             // we look for { errors : { needs_confirm : true }} in the response.
46863             if (
46864                 (typeof(action.result) != 'undefined')  &&
46865                 (typeof(action.result.errors) != 'undefined')  &&
46866                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46867            ){
46868                 var _t = this;
46869                 Roo.MessageBox.confirm(
46870                     "Change requires confirmation",
46871                     action.result.errorMsg,
46872                     function(r) {
46873                         if (r != 'yes') {
46874                             return;
46875                         }
46876                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
46877                     }
46878                     
46879                 );
46880                 
46881                 
46882                 
46883                 return;
46884             }
46885             
46886             Roo.callback(o.failure, o.scope, [this, action]);
46887             // show an error message if no failed handler is set..
46888             if (!this.hasListener('actionfailed')) {
46889                 Roo.MessageBox.alert("Error",
46890                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
46891                         action.result.errorMsg :
46892                         "Saving Failed, please check your entries or try again"
46893                 );
46894             }
46895             
46896             this.fireEvent('actionfailed', this, action);
46897         }
46898         
46899     },
46900
46901     /**
46902      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
46903      * @param {String} id The value to search for
46904      * @return Field
46905      */
46906     findField : function(id){
46907         var field = this.items.get(id);
46908         if(!field){
46909             this.items.each(function(f){
46910                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
46911                     field = f;
46912                     return false;
46913                 }
46914             });
46915         }
46916         return field || null;
46917     },
46918
46919     /**
46920      * Add a secondary form to this one, 
46921      * Used to provide tabbed forms. One form is primary, with hidden values 
46922      * which mirror the elements from the other forms.
46923      * 
46924      * @param {Roo.form.Form} form to add.
46925      * 
46926      */
46927     addForm : function(form)
46928     {
46929        
46930         if (this.childForms.indexOf(form) > -1) {
46931             // already added..
46932             return;
46933         }
46934         this.childForms.push(form);
46935         var n = '';
46936         Roo.each(form.allItems, function (fe) {
46937             
46938             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
46939             if (this.findField(n)) { // already added..
46940                 return;
46941             }
46942             var add = new Roo.form.Hidden({
46943                 name : n
46944             });
46945             add.render(this.el);
46946             
46947             this.add( add );
46948         }, this);
46949         
46950     },
46951     /**
46952      * Mark fields in this form invalid in bulk.
46953      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
46954      * @return {BasicForm} this
46955      */
46956     markInvalid : function(errors){
46957         if(errors instanceof Array){
46958             for(var i = 0, len = errors.length; i < len; i++){
46959                 var fieldError = errors[i];
46960                 var f = this.findField(fieldError.id);
46961                 if(f){
46962                     f.markInvalid(fieldError.msg);
46963                 }
46964             }
46965         }else{
46966             var field, id;
46967             for(id in errors){
46968                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
46969                     field.markInvalid(errors[id]);
46970                 }
46971             }
46972         }
46973         Roo.each(this.childForms || [], function (f) {
46974             f.markInvalid(errors);
46975         });
46976         
46977         return this;
46978     },
46979
46980     /**
46981      * Set values for fields in this form in bulk.
46982      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
46983      * @return {BasicForm} this
46984      */
46985     setValues : function(values){
46986         if(values instanceof Array){ // array of objects
46987             for(var i = 0, len = values.length; i < len; i++){
46988                 var v = values[i];
46989                 var f = this.findField(v.id);
46990                 if(f){
46991                     f.setValue(v.value);
46992                     if(this.trackResetOnLoad){
46993                         f.originalValue = f.getValue();
46994                     }
46995                 }
46996             }
46997         }else{ // object hash
46998             var field, id;
46999             for(id in values){
47000                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47001                     
47002                     if (field.setFromData && 
47003                         field.valueField && 
47004                         field.displayField &&
47005                         // combos' with local stores can 
47006                         // be queried via setValue()
47007                         // to set their value..
47008                         (field.store && !field.store.isLocal)
47009                         ) {
47010                         // it's a combo
47011                         var sd = { };
47012                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47013                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47014                         field.setFromData(sd);
47015                         
47016                     } else {
47017                         field.setValue(values[id]);
47018                     }
47019                     
47020                     
47021                     if(this.trackResetOnLoad){
47022                         field.originalValue = field.getValue();
47023                     }
47024                 }
47025             }
47026         }
47027         this.resetHasChanged();
47028         
47029         
47030         Roo.each(this.childForms || [], function (f) {
47031             f.setValues(values);
47032             f.resetHasChanged();
47033         });
47034                 
47035         return this;
47036     },
47037
47038     /**
47039      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47040      * they are returned as an array.
47041      * @param {Boolean} asString
47042      * @return {Object}
47043      */
47044     getValues : function(asString){
47045         if (this.childForms) {
47046             // copy values from the child forms
47047             Roo.each(this.childForms, function (f) {
47048                 this.setValues(f.getValues());
47049             }, this);
47050         }
47051         
47052         
47053         
47054         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47055         if(asString === true){
47056             return fs;
47057         }
47058         return Roo.urlDecode(fs);
47059     },
47060     
47061     /**
47062      * Returns the fields in this form as an object with key/value pairs. 
47063      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47064      * @return {Object}
47065      */
47066     getFieldValues : function(with_hidden)
47067     {
47068         if (this.childForms) {
47069             // copy values from the child forms
47070             // should this call getFieldValues - probably not as we do not currently copy
47071             // hidden fields when we generate..
47072             Roo.each(this.childForms, function (f) {
47073                 this.setValues(f.getValues());
47074             }, this);
47075         }
47076         
47077         var ret = {};
47078         this.items.each(function(f){
47079             if (!f.getName()) {
47080                 return;
47081             }
47082             var v = f.getValue();
47083             if (f.inputType =='radio') {
47084                 if (typeof(ret[f.getName()]) == 'undefined') {
47085                     ret[f.getName()] = ''; // empty..
47086                 }
47087                 
47088                 if (!f.el.dom.checked) {
47089                     return;
47090                     
47091                 }
47092                 v = f.el.dom.value;
47093                 
47094             }
47095             
47096             // not sure if this supported any more..
47097             if ((typeof(v) == 'object') && f.getRawValue) {
47098                 v = f.getRawValue() ; // dates..
47099             }
47100             // combo boxes where name != hiddenName...
47101             if (f.name != f.getName()) {
47102                 ret[f.name] = f.getRawValue();
47103             }
47104             ret[f.getName()] = v;
47105         });
47106         
47107         return ret;
47108     },
47109
47110     /**
47111      * Clears all invalid messages in this form.
47112      * @return {BasicForm} this
47113      */
47114     clearInvalid : function(){
47115         this.items.each(function(f){
47116            f.clearInvalid();
47117         });
47118         
47119         Roo.each(this.childForms || [], function (f) {
47120             f.clearInvalid();
47121         });
47122         
47123         
47124         return this;
47125     },
47126
47127     /**
47128      * Resets this form.
47129      * @return {BasicForm} this
47130      */
47131     reset : function(){
47132         this.items.each(function(f){
47133             f.reset();
47134         });
47135         
47136         Roo.each(this.childForms || [], function (f) {
47137             f.reset();
47138         });
47139         this.resetHasChanged();
47140         
47141         return this;
47142     },
47143
47144     /**
47145      * Add Roo.form components to this form.
47146      * @param {Field} field1
47147      * @param {Field} field2 (optional)
47148      * @param {Field} etc (optional)
47149      * @return {BasicForm} this
47150      */
47151     add : function(){
47152         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47153         return this;
47154     },
47155
47156
47157     /**
47158      * Removes a field from the items collection (does NOT remove its markup).
47159      * @param {Field} field
47160      * @return {BasicForm} this
47161      */
47162     remove : function(field){
47163         this.items.remove(field);
47164         return this;
47165     },
47166
47167     /**
47168      * Looks at the fields in this form, checks them for an id attribute,
47169      * and calls applyTo on the existing dom element with that id.
47170      * @return {BasicForm} this
47171      */
47172     render : function(){
47173         this.items.each(function(f){
47174             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47175                 f.applyTo(f.id);
47176             }
47177         });
47178         return this;
47179     },
47180
47181     /**
47182      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47183      * @param {Object} values
47184      * @return {BasicForm} this
47185      */
47186     applyToFields : function(o){
47187         this.items.each(function(f){
47188            Roo.apply(f, o);
47189         });
47190         return this;
47191     },
47192
47193     /**
47194      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47195      * @param {Object} values
47196      * @return {BasicForm} this
47197      */
47198     applyIfToFields : function(o){
47199         this.items.each(function(f){
47200            Roo.applyIf(f, o);
47201         });
47202         return this;
47203     }
47204 });
47205
47206 // back compat
47207 Roo.BasicForm = Roo.form.BasicForm;/*
47208  * Based on:
47209  * Ext JS Library 1.1.1
47210  * Copyright(c) 2006-2007, Ext JS, LLC.
47211  *
47212  * Originally Released Under LGPL - original licence link has changed is not relivant.
47213  *
47214  * Fork - LGPL
47215  * <script type="text/javascript">
47216  */
47217
47218 /**
47219  * @class Roo.form.Form
47220  * @extends Roo.form.BasicForm
47221  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47222  * @constructor
47223  * @param {Object} config Configuration options
47224  */
47225 Roo.form.Form = function(config){
47226     var xitems =  [];
47227     if (config.items) {
47228         xitems = config.items;
47229         delete config.items;
47230     }
47231    
47232     
47233     Roo.form.Form.superclass.constructor.call(this, null, config);
47234     this.url = this.url || this.action;
47235     if(!this.root){
47236         this.root = new Roo.form.Layout(Roo.applyIf({
47237             id: Roo.id()
47238         }, config));
47239     }
47240     this.active = this.root;
47241     /**
47242      * Array of all the buttons that have been added to this form via {@link addButton}
47243      * @type Array
47244      */
47245     this.buttons = [];
47246     this.allItems = [];
47247     this.addEvents({
47248         /**
47249          * @event clientvalidation
47250          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47251          * @param {Form} this
47252          * @param {Boolean} valid true if the form has passed client-side validation
47253          */
47254         clientvalidation: true,
47255         /**
47256          * @event rendered
47257          * Fires when the form is rendered
47258          * @param {Roo.form.Form} form
47259          */
47260         rendered : true
47261     });
47262     
47263     if (this.progressUrl) {
47264             // push a hidden field onto the list of fields..
47265             this.addxtype( {
47266                     xns: Roo.form, 
47267                     xtype : 'Hidden', 
47268                     name : 'UPLOAD_IDENTIFIER' 
47269             });
47270         }
47271         
47272     
47273     Roo.each(xitems, this.addxtype, this);
47274     
47275     
47276     
47277 };
47278
47279 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47280     /**
47281      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47282      */
47283     /**
47284      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47285      */
47286     /**
47287      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47288      */
47289     buttonAlign:'center',
47290
47291     /**
47292      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47293      */
47294     minButtonWidth:75,
47295
47296     /**
47297      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47298      * This property cascades to child containers if not set.
47299      */
47300     labelAlign:'left',
47301
47302     /**
47303      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47304      * fires a looping event with that state. This is required to bind buttons to the valid
47305      * state using the config value formBind:true on the button.
47306      */
47307     monitorValid : false,
47308
47309     /**
47310      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47311      */
47312     monitorPoll : 200,
47313     
47314     /**
47315      * @cfg {String} progressUrl - Url to return progress data 
47316      */
47317     
47318     progressUrl : false,
47319   
47320     /**
47321      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47322      * fields are added and the column is closed. If no fields are passed the column remains open
47323      * until end() is called.
47324      * @param {Object} config The config to pass to the column
47325      * @param {Field} field1 (optional)
47326      * @param {Field} field2 (optional)
47327      * @param {Field} etc (optional)
47328      * @return Column The column container object
47329      */
47330     column : function(c){
47331         var col = new Roo.form.Column(c);
47332         this.start(col);
47333         if(arguments.length > 1){ // duplicate code required because of Opera
47334             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47335             this.end();
47336         }
47337         return col;
47338     },
47339
47340     /**
47341      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47342      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47343      * until end() is called.
47344      * @param {Object} config The config to pass to the fieldset
47345      * @param {Field} field1 (optional)
47346      * @param {Field} field2 (optional)
47347      * @param {Field} etc (optional)
47348      * @return FieldSet The fieldset container object
47349      */
47350     fieldset : function(c){
47351         var fs = new Roo.form.FieldSet(c);
47352         this.start(fs);
47353         if(arguments.length > 1){ // duplicate code required because of Opera
47354             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47355             this.end();
47356         }
47357         return fs;
47358     },
47359
47360     /**
47361      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47362      * fields are added and the container is closed. If no fields are passed the container remains open
47363      * until end() is called.
47364      * @param {Object} config The config to pass to the Layout
47365      * @param {Field} field1 (optional)
47366      * @param {Field} field2 (optional)
47367      * @param {Field} etc (optional)
47368      * @return Layout The container object
47369      */
47370     container : function(c){
47371         var l = new Roo.form.Layout(c);
47372         this.start(l);
47373         if(arguments.length > 1){ // duplicate code required because of Opera
47374             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47375             this.end();
47376         }
47377         return l;
47378     },
47379
47380     /**
47381      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47382      * @param {Object} container A Roo.form.Layout or subclass of Layout
47383      * @return {Form} this
47384      */
47385     start : function(c){
47386         // cascade label info
47387         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47388         this.active.stack.push(c);
47389         c.ownerCt = this.active;
47390         this.active = c;
47391         return this;
47392     },
47393
47394     /**
47395      * Closes the current open container
47396      * @return {Form} this
47397      */
47398     end : function(){
47399         if(this.active == this.root){
47400             return this;
47401         }
47402         this.active = this.active.ownerCt;
47403         return this;
47404     },
47405
47406     /**
47407      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47408      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47409      * as the label of the field.
47410      * @param {Field} field1
47411      * @param {Field} field2 (optional)
47412      * @param {Field} etc. (optional)
47413      * @return {Form} this
47414      */
47415     add : function(){
47416         this.active.stack.push.apply(this.active.stack, arguments);
47417         this.allItems.push.apply(this.allItems,arguments);
47418         var r = [];
47419         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47420             if(a[i].isFormField){
47421                 r.push(a[i]);
47422             }
47423         }
47424         if(r.length > 0){
47425             Roo.form.Form.superclass.add.apply(this, r);
47426         }
47427         return this;
47428     },
47429     
47430
47431     
47432     
47433     
47434      /**
47435      * Find any element that has been added to a form, using it's ID or name
47436      * This can include framesets, columns etc. along with regular fields..
47437      * @param {String} id - id or name to find.
47438      
47439      * @return {Element} e - or false if nothing found.
47440      */
47441     findbyId : function(id)
47442     {
47443         var ret = false;
47444         if (!id) {
47445             return ret;
47446         }
47447         Roo.each(this.allItems, function(f){
47448             if (f.id == id || f.name == id ){
47449                 ret = f;
47450                 return false;
47451             }
47452         });
47453         return ret;
47454     },
47455
47456     
47457     
47458     /**
47459      * Render this form into the passed container. This should only be called once!
47460      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47461      * @return {Form} this
47462      */
47463     render : function(ct)
47464     {
47465         
47466         
47467         
47468         ct = Roo.get(ct);
47469         var o = this.autoCreate || {
47470             tag: 'form',
47471             method : this.method || 'POST',
47472             id : this.id || Roo.id()
47473         };
47474         this.initEl(ct.createChild(o));
47475
47476         this.root.render(this.el);
47477         
47478        
47479              
47480         this.items.each(function(f){
47481             f.render('x-form-el-'+f.id);
47482         });
47483
47484         if(this.buttons.length > 0){
47485             // tables are required to maintain order and for correct IE layout
47486             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47487                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47488                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47489             }}, null, true);
47490             var tr = tb.getElementsByTagName('tr')[0];
47491             for(var i = 0, len = this.buttons.length; i < len; i++) {
47492                 var b = this.buttons[i];
47493                 var td = document.createElement('td');
47494                 td.className = 'x-form-btn-td';
47495                 b.render(tr.appendChild(td));
47496             }
47497         }
47498         if(this.monitorValid){ // initialize after render
47499             this.startMonitoring();
47500         }
47501         this.fireEvent('rendered', this);
47502         return this;
47503     },
47504
47505     /**
47506      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47507      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47508      * object or a valid Roo.DomHelper element config
47509      * @param {Function} handler The function called when the button is clicked
47510      * @param {Object} scope (optional) The scope of the handler function
47511      * @return {Roo.Button}
47512      */
47513     addButton : function(config, handler, scope){
47514         var bc = {
47515             handler: handler,
47516             scope: scope,
47517             minWidth: this.minButtonWidth,
47518             hideParent:true
47519         };
47520         if(typeof config == "string"){
47521             bc.text = config;
47522         }else{
47523             Roo.apply(bc, config);
47524         }
47525         var btn = new Roo.Button(null, bc);
47526         this.buttons.push(btn);
47527         return btn;
47528     },
47529
47530      /**
47531      * Adds a series of form elements (using the xtype property as the factory method.
47532      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47533      * @param {Object} config 
47534      */
47535     
47536     addxtype : function()
47537     {
47538         var ar = Array.prototype.slice.call(arguments, 0);
47539         var ret = false;
47540         for(var i = 0; i < ar.length; i++) {
47541             if (!ar[i]) {
47542                 continue; // skip -- if this happends something invalid got sent, we 
47543                 // should ignore it, as basically that interface element will not show up
47544                 // and that should be pretty obvious!!
47545             }
47546             
47547             if (Roo.form[ar[i].xtype]) {
47548                 ar[i].form = this;
47549                 var fe = Roo.factory(ar[i], Roo.form);
47550                 if (!ret) {
47551                     ret = fe;
47552                 }
47553                 fe.form = this;
47554                 if (fe.store) {
47555                     fe.store.form = this;
47556                 }
47557                 if (fe.isLayout) {  
47558                          
47559                     this.start(fe);
47560                     this.allItems.push(fe);
47561                     if (fe.items && fe.addxtype) {
47562                         fe.addxtype.apply(fe, fe.items);
47563                         delete fe.items;
47564                     }
47565                      this.end();
47566                     continue;
47567                 }
47568                 
47569                 
47570                  
47571                 this.add(fe);
47572               //  console.log('adding ' + ar[i].xtype);
47573             }
47574             if (ar[i].xtype == 'Button') {  
47575                 //console.log('adding button');
47576                 //console.log(ar[i]);
47577                 this.addButton(ar[i]);
47578                 this.allItems.push(fe);
47579                 continue;
47580             }
47581             
47582             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47583                 alert('end is not supported on xtype any more, use items');
47584             //    this.end();
47585             //    //console.log('adding end');
47586             }
47587             
47588         }
47589         return ret;
47590     },
47591     
47592     /**
47593      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47594      * option "monitorValid"
47595      */
47596     startMonitoring : function(){
47597         if(!this.bound){
47598             this.bound = true;
47599             Roo.TaskMgr.start({
47600                 run : this.bindHandler,
47601                 interval : this.monitorPoll || 200,
47602                 scope: this
47603             });
47604         }
47605     },
47606
47607     /**
47608      * Stops monitoring of the valid state of this form
47609      */
47610     stopMonitoring : function(){
47611         this.bound = false;
47612     },
47613
47614     // private
47615     bindHandler : function(){
47616         if(!this.bound){
47617             return false; // stops binding
47618         }
47619         var valid = true;
47620         this.items.each(function(f){
47621             if(!f.isValid(true)){
47622                 valid = false;
47623                 return false;
47624             }
47625         });
47626         for(var i = 0, len = this.buttons.length; i < len; i++){
47627             var btn = this.buttons[i];
47628             if(btn.formBind === true && btn.disabled === valid){
47629                 btn.setDisabled(!valid);
47630             }
47631         }
47632         this.fireEvent('clientvalidation', this, valid);
47633     }
47634     
47635     
47636     
47637     
47638     
47639     
47640     
47641     
47642 });
47643
47644
47645 // back compat
47646 Roo.Form = Roo.form.Form;
47647 /*
47648  * Based on:
47649  * Ext JS Library 1.1.1
47650  * Copyright(c) 2006-2007, Ext JS, LLC.
47651  *
47652  * Originally Released Under LGPL - original licence link has changed is not relivant.
47653  *
47654  * Fork - LGPL
47655  * <script type="text/javascript">
47656  */
47657
47658 // as we use this in bootstrap.
47659 Roo.namespace('Roo.form');
47660  /**
47661  * @class Roo.form.Action
47662  * Internal Class used to handle form actions
47663  * @constructor
47664  * @param {Roo.form.BasicForm} el The form element or its id
47665  * @param {Object} config Configuration options
47666  */
47667
47668  
47669  
47670 // define the action interface
47671 Roo.form.Action = function(form, options){
47672     this.form = form;
47673     this.options = options || {};
47674 };
47675 /**
47676  * Client Validation Failed
47677  * @const 
47678  */
47679 Roo.form.Action.CLIENT_INVALID = 'client';
47680 /**
47681  * Server Validation Failed
47682  * @const 
47683  */
47684 Roo.form.Action.SERVER_INVALID = 'server';
47685  /**
47686  * Connect to Server Failed
47687  * @const 
47688  */
47689 Roo.form.Action.CONNECT_FAILURE = 'connect';
47690 /**
47691  * Reading Data from Server Failed
47692  * @const 
47693  */
47694 Roo.form.Action.LOAD_FAILURE = 'load';
47695
47696 Roo.form.Action.prototype = {
47697     type : 'default',
47698     failureType : undefined,
47699     response : undefined,
47700     result : undefined,
47701
47702     // interface method
47703     run : function(options){
47704
47705     },
47706
47707     // interface method
47708     success : function(response){
47709
47710     },
47711
47712     // interface method
47713     handleResponse : function(response){
47714
47715     },
47716
47717     // default connection failure
47718     failure : function(response){
47719         
47720         this.response = response;
47721         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47722         this.form.afterAction(this, false);
47723     },
47724
47725     processResponse : function(response){
47726         this.response = response;
47727         if(!response.responseText){
47728             return true;
47729         }
47730         this.result = this.handleResponse(response);
47731         return this.result;
47732     },
47733
47734     // utility functions used internally
47735     getUrl : function(appendParams){
47736         var url = this.options.url || this.form.url || this.form.el.dom.action;
47737         if(appendParams){
47738             var p = this.getParams();
47739             if(p){
47740                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47741             }
47742         }
47743         return url;
47744     },
47745
47746     getMethod : function(){
47747         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47748     },
47749
47750     getParams : function(){
47751         var bp = this.form.baseParams;
47752         var p = this.options.params;
47753         if(p){
47754             if(typeof p == "object"){
47755                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47756             }else if(typeof p == 'string' && bp){
47757                 p += '&' + Roo.urlEncode(bp);
47758             }
47759         }else if(bp){
47760             p = Roo.urlEncode(bp);
47761         }
47762         return p;
47763     },
47764
47765     createCallback : function(){
47766         return {
47767             success: this.success,
47768             failure: this.failure,
47769             scope: this,
47770             timeout: (this.form.timeout*1000),
47771             upload: this.form.fileUpload ? this.success : undefined
47772         };
47773     }
47774 };
47775
47776 Roo.form.Action.Submit = function(form, options){
47777     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47778 };
47779
47780 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47781     type : 'submit',
47782
47783     haveProgress : false,
47784     uploadComplete : false,
47785     
47786     // uploadProgress indicator.
47787     uploadProgress : function()
47788     {
47789         if (!this.form.progressUrl) {
47790             return;
47791         }
47792         
47793         if (!this.haveProgress) {
47794             Roo.MessageBox.progress("Uploading", "Uploading");
47795         }
47796         if (this.uploadComplete) {
47797            Roo.MessageBox.hide();
47798            return;
47799         }
47800         
47801         this.haveProgress = true;
47802    
47803         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47804         
47805         var c = new Roo.data.Connection();
47806         c.request({
47807             url : this.form.progressUrl,
47808             params: {
47809                 id : uid
47810             },
47811             method: 'GET',
47812             success : function(req){
47813                //console.log(data);
47814                 var rdata = false;
47815                 var edata;
47816                 try  {
47817                    rdata = Roo.decode(req.responseText)
47818                 } catch (e) {
47819                     Roo.log("Invalid data from server..");
47820                     Roo.log(edata);
47821                     return;
47822                 }
47823                 if (!rdata || !rdata.success) {
47824                     Roo.log(rdata);
47825                     Roo.MessageBox.alert(Roo.encode(rdata));
47826                     return;
47827                 }
47828                 var data = rdata.data;
47829                 
47830                 if (this.uploadComplete) {
47831                    Roo.MessageBox.hide();
47832                    return;
47833                 }
47834                    
47835                 if (data){
47836                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47837                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47838                     );
47839                 }
47840                 this.uploadProgress.defer(2000,this);
47841             },
47842        
47843             failure: function(data) {
47844                 Roo.log('progress url failed ');
47845                 Roo.log(data);
47846             },
47847             scope : this
47848         });
47849            
47850     },
47851     
47852     
47853     run : function()
47854     {
47855         // run get Values on the form, so it syncs any secondary forms.
47856         this.form.getValues();
47857         
47858         var o = this.options;
47859         var method = this.getMethod();
47860         var isPost = method == 'POST';
47861         if(o.clientValidation === false || this.form.isValid()){
47862             
47863             if (this.form.progressUrl) {
47864                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
47865                     (new Date() * 1) + '' + Math.random());
47866                     
47867             } 
47868             
47869             
47870             Roo.Ajax.request(Roo.apply(this.createCallback(), {
47871                 form:this.form.el.dom,
47872                 url:this.getUrl(!isPost),
47873                 method: method,
47874                 params:isPost ? this.getParams() : null,
47875                 isUpload: this.form.fileUpload
47876             }));
47877             
47878             this.uploadProgress();
47879
47880         }else if (o.clientValidation !== false){ // client validation failed
47881             this.failureType = Roo.form.Action.CLIENT_INVALID;
47882             this.form.afterAction(this, false);
47883         }
47884     },
47885
47886     success : function(response)
47887     {
47888         this.uploadComplete= true;
47889         if (this.haveProgress) {
47890             Roo.MessageBox.hide();
47891         }
47892         
47893         
47894         var result = this.processResponse(response);
47895         if(result === true || result.success){
47896             this.form.afterAction(this, true);
47897             return;
47898         }
47899         if(result.errors){
47900             this.form.markInvalid(result.errors);
47901             this.failureType = Roo.form.Action.SERVER_INVALID;
47902         }
47903         this.form.afterAction(this, false);
47904     },
47905     failure : function(response)
47906     {
47907         this.uploadComplete= true;
47908         if (this.haveProgress) {
47909             Roo.MessageBox.hide();
47910         }
47911         
47912         this.response = response;
47913         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47914         this.form.afterAction(this, false);
47915     },
47916     
47917     handleResponse : function(response){
47918         if(this.form.errorReader){
47919             var rs = this.form.errorReader.read(response);
47920             var errors = [];
47921             if(rs.records){
47922                 for(var i = 0, len = rs.records.length; i < len; i++) {
47923                     var r = rs.records[i];
47924                     errors[i] = r.data;
47925                 }
47926             }
47927             if(errors.length < 1){
47928                 errors = null;
47929             }
47930             return {
47931                 success : rs.success,
47932                 errors : errors
47933             };
47934         }
47935         var ret = false;
47936         try {
47937             ret = Roo.decode(response.responseText);
47938         } catch (e) {
47939             ret = {
47940                 success: false,
47941                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
47942                 errors : []
47943             };
47944         }
47945         return ret;
47946         
47947     }
47948 });
47949
47950
47951 Roo.form.Action.Load = function(form, options){
47952     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
47953     this.reader = this.form.reader;
47954 };
47955
47956 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
47957     type : 'load',
47958
47959     run : function(){
47960         
47961         Roo.Ajax.request(Roo.apply(
47962                 this.createCallback(), {
47963                     method:this.getMethod(),
47964                     url:this.getUrl(false),
47965                     params:this.getParams()
47966         }));
47967     },
47968
47969     success : function(response){
47970         
47971         var result = this.processResponse(response);
47972         if(result === true || !result.success || !result.data){
47973             this.failureType = Roo.form.Action.LOAD_FAILURE;
47974             this.form.afterAction(this, false);
47975             return;
47976         }
47977         this.form.clearInvalid();
47978         this.form.setValues(result.data);
47979         this.form.afterAction(this, true);
47980     },
47981
47982     handleResponse : function(response){
47983         if(this.form.reader){
47984             var rs = this.form.reader.read(response);
47985             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
47986             return {
47987                 success : rs.success,
47988                 data : data
47989             };
47990         }
47991         return Roo.decode(response.responseText);
47992     }
47993 });
47994
47995 Roo.form.Action.ACTION_TYPES = {
47996     'load' : Roo.form.Action.Load,
47997     'submit' : Roo.form.Action.Submit
47998 };/*
47999  * Based on:
48000  * Ext JS Library 1.1.1
48001  * Copyright(c) 2006-2007, Ext JS, LLC.
48002  *
48003  * Originally Released Under LGPL - original licence link has changed is not relivant.
48004  *
48005  * Fork - LGPL
48006  * <script type="text/javascript">
48007  */
48008  
48009 /**
48010  * @class Roo.form.Layout
48011  * @extends Roo.Component
48012  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
48013  * @constructor
48014  * @param {Object} config Configuration options
48015  */
48016 Roo.form.Layout = function(config){
48017     var xitems = [];
48018     if (config.items) {
48019         xitems = config.items;
48020         delete config.items;
48021     }
48022     Roo.form.Layout.superclass.constructor.call(this, config);
48023     this.stack = [];
48024     Roo.each(xitems, this.addxtype, this);
48025      
48026 };
48027
48028 Roo.extend(Roo.form.Layout, Roo.Component, {
48029     /**
48030      * @cfg {String/Object} autoCreate
48031      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48032      */
48033     /**
48034      * @cfg {String/Object/Function} style
48035      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48036      * a function which returns such a specification.
48037      */
48038     /**
48039      * @cfg {String} labelAlign
48040      * Valid values are "left," "top" and "right" (defaults to "left")
48041      */
48042     /**
48043      * @cfg {Number} labelWidth
48044      * Fixed width in pixels of all field labels (defaults to undefined)
48045      */
48046     /**
48047      * @cfg {Boolean} clear
48048      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48049      */
48050     clear : true,
48051     /**
48052      * @cfg {String} labelSeparator
48053      * The separator to use after field labels (defaults to ':')
48054      */
48055     labelSeparator : ':',
48056     /**
48057      * @cfg {Boolean} hideLabels
48058      * True to suppress the display of field labels in this layout (defaults to false)
48059      */
48060     hideLabels : false,
48061
48062     // private
48063     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48064     
48065     isLayout : true,
48066     
48067     // private
48068     onRender : function(ct, position){
48069         if(this.el){ // from markup
48070             this.el = Roo.get(this.el);
48071         }else {  // generate
48072             var cfg = this.getAutoCreate();
48073             this.el = ct.createChild(cfg, position);
48074         }
48075         if(this.style){
48076             this.el.applyStyles(this.style);
48077         }
48078         if(this.labelAlign){
48079             this.el.addClass('x-form-label-'+this.labelAlign);
48080         }
48081         if(this.hideLabels){
48082             this.labelStyle = "display:none";
48083             this.elementStyle = "padding-left:0;";
48084         }else{
48085             if(typeof this.labelWidth == 'number'){
48086                 this.labelStyle = "width:"+this.labelWidth+"px;";
48087                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48088             }
48089             if(this.labelAlign == 'top'){
48090                 this.labelStyle = "width:auto;";
48091                 this.elementStyle = "padding-left:0;";
48092             }
48093         }
48094         var stack = this.stack;
48095         var slen = stack.length;
48096         if(slen > 0){
48097             if(!this.fieldTpl){
48098                 var t = new Roo.Template(
48099                     '<div class="x-form-item {5}">',
48100                         '<label for="{0}" style="{2}">{1}{4}</label>',
48101                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48102                         '</div>',
48103                     '</div><div class="x-form-clear-left"></div>'
48104                 );
48105                 t.disableFormats = true;
48106                 t.compile();
48107                 Roo.form.Layout.prototype.fieldTpl = t;
48108             }
48109             for(var i = 0; i < slen; i++) {
48110                 if(stack[i].isFormField){
48111                     this.renderField(stack[i]);
48112                 }else{
48113                     this.renderComponent(stack[i]);
48114                 }
48115             }
48116         }
48117         if(this.clear){
48118             this.el.createChild({cls:'x-form-clear'});
48119         }
48120     },
48121
48122     // private
48123     renderField : function(f){
48124         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48125                f.id, //0
48126                f.fieldLabel, //1
48127                f.labelStyle||this.labelStyle||'', //2
48128                this.elementStyle||'', //3
48129                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48130                f.itemCls||this.itemCls||''  //5
48131        ], true).getPrevSibling());
48132     },
48133
48134     // private
48135     renderComponent : function(c){
48136         c.render(c.isLayout ? this.el : this.el.createChild());    
48137     },
48138     /**
48139      * Adds a object form elements (using the xtype property as the factory method.)
48140      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48141      * @param {Object} config 
48142      */
48143     addxtype : function(o)
48144     {
48145         // create the lement.
48146         o.form = this.form;
48147         var fe = Roo.factory(o, Roo.form);
48148         this.form.allItems.push(fe);
48149         this.stack.push(fe);
48150         
48151         if (fe.isFormField) {
48152             this.form.items.add(fe);
48153         }
48154          
48155         return fe;
48156     }
48157 });
48158
48159 /**
48160  * @class Roo.form.Column
48161  * @extends Roo.form.Layout
48162  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48163  * @constructor
48164  * @param {Object} config Configuration options
48165  */
48166 Roo.form.Column = function(config){
48167     Roo.form.Column.superclass.constructor.call(this, config);
48168 };
48169
48170 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48171     /**
48172      * @cfg {Number/String} width
48173      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48174      */
48175     /**
48176      * @cfg {String/Object} autoCreate
48177      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48178      */
48179
48180     // private
48181     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48182
48183     // private
48184     onRender : function(ct, position){
48185         Roo.form.Column.superclass.onRender.call(this, ct, position);
48186         if(this.width){
48187             this.el.setWidth(this.width);
48188         }
48189     }
48190 });
48191
48192
48193 /**
48194  * @class Roo.form.Row
48195  * @extends Roo.form.Layout
48196  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48197  * @constructor
48198  * @param {Object} config Configuration options
48199  */
48200
48201  
48202 Roo.form.Row = function(config){
48203     Roo.form.Row.superclass.constructor.call(this, config);
48204 };
48205  
48206 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48207       /**
48208      * @cfg {Number/String} width
48209      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48210      */
48211     /**
48212      * @cfg {Number/String} height
48213      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48214      */
48215     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48216     
48217     padWidth : 20,
48218     // private
48219     onRender : function(ct, position){
48220         //console.log('row render');
48221         if(!this.rowTpl){
48222             var t = new Roo.Template(
48223                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48224                     '<label for="{0}" style="{2}">{1}{4}</label>',
48225                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48226                     '</div>',
48227                 '</div>'
48228             );
48229             t.disableFormats = true;
48230             t.compile();
48231             Roo.form.Layout.prototype.rowTpl = t;
48232         }
48233         this.fieldTpl = this.rowTpl;
48234         
48235         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48236         var labelWidth = 100;
48237         
48238         if ((this.labelAlign != 'top')) {
48239             if (typeof this.labelWidth == 'number') {
48240                 labelWidth = this.labelWidth
48241             }
48242             this.padWidth =  20 + labelWidth;
48243             
48244         }
48245         
48246         Roo.form.Column.superclass.onRender.call(this, ct, position);
48247         if(this.width){
48248             this.el.setWidth(this.width);
48249         }
48250         if(this.height){
48251             this.el.setHeight(this.height);
48252         }
48253     },
48254     
48255     // private
48256     renderField : function(f){
48257         f.fieldEl = this.fieldTpl.append(this.el, [
48258                f.id, f.fieldLabel,
48259                f.labelStyle||this.labelStyle||'',
48260                this.elementStyle||'',
48261                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48262                f.itemCls||this.itemCls||'',
48263                f.width ? f.width + this.padWidth : 160 + this.padWidth
48264        ],true);
48265     }
48266 });
48267  
48268
48269 /**
48270  * @class Roo.form.FieldSet
48271  * @extends Roo.form.Layout
48272  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48273  * @constructor
48274  * @param {Object} config Configuration options
48275  */
48276 Roo.form.FieldSet = function(config){
48277     Roo.form.FieldSet.superclass.constructor.call(this, config);
48278 };
48279
48280 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48281     /**
48282      * @cfg {String} legend
48283      * The text to display as the legend for the FieldSet (defaults to '')
48284      */
48285     /**
48286      * @cfg {String/Object} autoCreate
48287      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48288      */
48289
48290     // private
48291     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48292
48293     // private
48294     onRender : function(ct, position){
48295         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48296         if(this.legend){
48297             this.setLegend(this.legend);
48298         }
48299     },
48300
48301     // private
48302     setLegend : function(text){
48303         if(this.rendered){
48304             this.el.child('legend').update(text);
48305         }
48306     }
48307 });/*
48308  * Based on:
48309  * Ext JS Library 1.1.1
48310  * Copyright(c) 2006-2007, Ext JS, LLC.
48311  *
48312  * Originally Released Under LGPL - original licence link has changed is not relivant.
48313  *
48314  * Fork - LGPL
48315  * <script type="text/javascript">
48316  */
48317 /**
48318  * @class Roo.form.VTypes
48319  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48320  * @singleton
48321  */
48322 Roo.form.VTypes = function(){
48323     // closure these in so they are only created once.
48324     var alpha = /^[a-zA-Z_]+$/;
48325     var alphanum = /^[a-zA-Z0-9_]+$/;
48326     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48327     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48328
48329     // All these messages and functions are configurable
48330     return {
48331         /**
48332          * The function used to validate email addresses
48333          * @param {String} value The email address
48334          */
48335         'email' : function(v){
48336             return email.test(v);
48337         },
48338         /**
48339          * The error text to display when the email validation function returns false
48340          * @type String
48341          */
48342         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48343         /**
48344          * The keystroke filter mask to be applied on email input
48345          * @type RegExp
48346          */
48347         'emailMask' : /[a-z0-9_\.\-@]/i,
48348
48349         /**
48350          * The function used to validate URLs
48351          * @param {String} value The URL
48352          */
48353         'url' : function(v){
48354             return url.test(v);
48355         },
48356         /**
48357          * The error text to display when the url validation function returns false
48358          * @type String
48359          */
48360         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48361         
48362         /**
48363          * The function used to validate alpha values
48364          * @param {String} value The value
48365          */
48366         'alpha' : function(v){
48367             return alpha.test(v);
48368         },
48369         /**
48370          * The error text to display when the alpha validation function returns false
48371          * @type String
48372          */
48373         'alphaText' : 'This field should only contain letters and _',
48374         /**
48375          * The keystroke filter mask to be applied on alpha input
48376          * @type RegExp
48377          */
48378         'alphaMask' : /[a-z_]/i,
48379
48380         /**
48381          * The function used to validate alphanumeric values
48382          * @param {String} value The value
48383          */
48384         'alphanum' : function(v){
48385             return alphanum.test(v);
48386         },
48387         /**
48388          * The error text to display when the alphanumeric validation function returns false
48389          * @type String
48390          */
48391         'alphanumText' : 'This field should only contain letters, numbers and _',
48392         /**
48393          * The keystroke filter mask to be applied on alphanumeric input
48394          * @type RegExp
48395          */
48396         'alphanumMask' : /[a-z0-9_]/i
48397     };
48398 }();//<script type="text/javascript">
48399
48400 /**
48401  * @class Roo.form.FCKeditor
48402  * @extends Roo.form.TextArea
48403  * Wrapper around the FCKEditor http://www.fckeditor.net
48404  * @constructor
48405  * Creates a new FCKeditor
48406  * @param {Object} config Configuration options
48407  */
48408 Roo.form.FCKeditor = function(config){
48409     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48410     this.addEvents({
48411          /**
48412          * @event editorinit
48413          * Fired when the editor is initialized - you can add extra handlers here..
48414          * @param {FCKeditor} this
48415          * @param {Object} the FCK object.
48416          */
48417         editorinit : true
48418     });
48419     
48420     
48421 };
48422 Roo.form.FCKeditor.editors = { };
48423 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48424 {
48425     //defaultAutoCreate : {
48426     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48427     //},
48428     // private
48429     /**
48430      * @cfg {Object} fck options - see fck manual for details.
48431      */
48432     fckconfig : false,
48433     
48434     /**
48435      * @cfg {Object} fck toolbar set (Basic or Default)
48436      */
48437     toolbarSet : 'Basic',
48438     /**
48439      * @cfg {Object} fck BasePath
48440      */ 
48441     basePath : '/fckeditor/',
48442     
48443     
48444     frame : false,
48445     
48446     value : '',
48447     
48448    
48449     onRender : function(ct, position)
48450     {
48451         if(!this.el){
48452             this.defaultAutoCreate = {
48453                 tag: "textarea",
48454                 style:"width:300px;height:60px;",
48455                 autocomplete: "new-password"
48456             };
48457         }
48458         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48459         /*
48460         if(this.grow){
48461             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48462             if(this.preventScrollbars){
48463                 this.el.setStyle("overflow", "hidden");
48464             }
48465             this.el.setHeight(this.growMin);
48466         }
48467         */
48468         //console.log('onrender' + this.getId() );
48469         Roo.form.FCKeditor.editors[this.getId()] = this;
48470          
48471
48472         this.replaceTextarea() ;
48473         
48474     },
48475     
48476     getEditor : function() {
48477         return this.fckEditor;
48478     },
48479     /**
48480      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48481      * @param {Mixed} value The value to set
48482      */
48483     
48484     
48485     setValue : function(value)
48486     {
48487         //console.log('setValue: ' + value);
48488         
48489         if(typeof(value) == 'undefined') { // not sure why this is happending...
48490             return;
48491         }
48492         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48493         
48494         //if(!this.el || !this.getEditor()) {
48495         //    this.value = value;
48496             //this.setValue.defer(100,this,[value]);    
48497         //    return;
48498         //} 
48499         
48500         if(!this.getEditor()) {
48501             return;
48502         }
48503         
48504         this.getEditor().SetData(value);
48505         
48506         //
48507
48508     },
48509
48510     /**
48511      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48512      * @return {Mixed} value The field value
48513      */
48514     getValue : function()
48515     {
48516         
48517         if (this.frame && this.frame.dom.style.display == 'none') {
48518             return Roo.form.FCKeditor.superclass.getValue.call(this);
48519         }
48520         
48521         if(!this.el || !this.getEditor()) {
48522            
48523            // this.getValue.defer(100,this); 
48524             return this.value;
48525         }
48526        
48527         
48528         var value=this.getEditor().GetData();
48529         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48530         return Roo.form.FCKeditor.superclass.getValue.call(this);
48531         
48532
48533     },
48534
48535     /**
48536      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48537      * @return {Mixed} value The field value
48538      */
48539     getRawValue : function()
48540     {
48541         if (this.frame && this.frame.dom.style.display == 'none') {
48542             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48543         }
48544         
48545         if(!this.el || !this.getEditor()) {
48546             //this.getRawValue.defer(100,this); 
48547             return this.value;
48548             return;
48549         }
48550         
48551         
48552         
48553         var value=this.getEditor().GetData();
48554         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48555         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48556          
48557     },
48558     
48559     setSize : function(w,h) {
48560         
48561         
48562         
48563         //if (this.frame && this.frame.dom.style.display == 'none') {
48564         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48565         //    return;
48566         //}
48567         //if(!this.el || !this.getEditor()) {
48568         //    this.setSize.defer(100,this, [w,h]); 
48569         //    return;
48570         //}
48571         
48572         
48573         
48574         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48575         
48576         this.frame.dom.setAttribute('width', w);
48577         this.frame.dom.setAttribute('height', h);
48578         this.frame.setSize(w,h);
48579         
48580     },
48581     
48582     toggleSourceEdit : function(value) {
48583         
48584       
48585          
48586         this.el.dom.style.display = value ? '' : 'none';
48587         this.frame.dom.style.display = value ?  'none' : '';
48588         
48589     },
48590     
48591     
48592     focus: function(tag)
48593     {
48594         if (this.frame.dom.style.display == 'none') {
48595             return Roo.form.FCKeditor.superclass.focus.call(this);
48596         }
48597         if(!this.el || !this.getEditor()) {
48598             this.focus.defer(100,this, [tag]); 
48599             return;
48600         }
48601         
48602         
48603         
48604         
48605         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48606         this.getEditor().Focus();
48607         if (tgs.length) {
48608             if (!this.getEditor().Selection.GetSelection()) {
48609                 this.focus.defer(100,this, [tag]); 
48610                 return;
48611             }
48612             
48613             
48614             var r = this.getEditor().EditorDocument.createRange();
48615             r.setStart(tgs[0],0);
48616             r.setEnd(tgs[0],0);
48617             this.getEditor().Selection.GetSelection().removeAllRanges();
48618             this.getEditor().Selection.GetSelection().addRange(r);
48619             this.getEditor().Focus();
48620         }
48621         
48622     },
48623     
48624     
48625     
48626     replaceTextarea : function()
48627     {
48628         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48629             return ;
48630         }
48631         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48632         //{
48633             // We must check the elements firstly using the Id and then the name.
48634         var oTextarea = document.getElementById( this.getId() );
48635         
48636         var colElementsByName = document.getElementsByName( this.getId() ) ;
48637          
48638         oTextarea.style.display = 'none' ;
48639
48640         if ( oTextarea.tabIndex ) {            
48641             this.TabIndex = oTextarea.tabIndex ;
48642         }
48643         
48644         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48645         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48646         this.frame = Roo.get(this.getId() + '___Frame')
48647     },
48648     
48649     _getConfigHtml : function()
48650     {
48651         var sConfig = '' ;
48652
48653         for ( var o in this.fckconfig ) {
48654             sConfig += sConfig.length > 0  ? '&amp;' : '';
48655             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48656         }
48657
48658         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48659     },
48660     
48661     
48662     _getIFrameHtml : function()
48663     {
48664         var sFile = 'fckeditor.html' ;
48665         /* no idea what this is about..
48666         try
48667         {
48668             if ( (/fcksource=true/i).test( window.top.location.search ) )
48669                 sFile = 'fckeditor.original.html' ;
48670         }
48671         catch (e) { 
48672         */
48673
48674         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48675         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48676         
48677         
48678         var html = '<iframe id="' + this.getId() +
48679             '___Frame" src="' + sLink +
48680             '" width="' + this.width +
48681             '" height="' + this.height + '"' +
48682             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48683             ' frameborder="0" scrolling="no"></iframe>' ;
48684
48685         return html ;
48686     },
48687     
48688     _insertHtmlBefore : function( html, element )
48689     {
48690         if ( element.insertAdjacentHTML )       {
48691             // IE
48692             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48693         } else { // Gecko
48694             var oRange = document.createRange() ;
48695             oRange.setStartBefore( element ) ;
48696             var oFragment = oRange.createContextualFragment( html );
48697             element.parentNode.insertBefore( oFragment, element ) ;
48698         }
48699     }
48700     
48701     
48702   
48703     
48704     
48705     
48706     
48707
48708 });
48709
48710 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48711
48712 function FCKeditor_OnComplete(editorInstance){
48713     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48714     f.fckEditor = editorInstance;
48715     //console.log("loaded");
48716     f.fireEvent('editorinit', f, editorInstance);
48717
48718   
48719
48720  
48721
48722
48723
48724
48725
48726
48727
48728
48729
48730
48731
48732
48733
48734
48735
48736 //<script type="text/javascript">
48737 /**
48738  * @class Roo.form.GridField
48739  * @extends Roo.form.Field
48740  * Embed a grid (or editable grid into a form)
48741  * STATUS ALPHA
48742  * 
48743  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48744  * it needs 
48745  * xgrid.store = Roo.data.Store
48746  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48747  * xgrid.store.reader = Roo.data.JsonReader 
48748  * 
48749  * 
48750  * @constructor
48751  * Creates a new GridField
48752  * @param {Object} config Configuration options
48753  */
48754 Roo.form.GridField = function(config){
48755     Roo.form.GridField.superclass.constructor.call(this, config);
48756      
48757 };
48758
48759 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48760     /**
48761      * @cfg {Number} width  - used to restrict width of grid..
48762      */
48763     width : 100,
48764     /**
48765      * @cfg {Number} height - used to restrict height of grid..
48766      */
48767     height : 50,
48768      /**
48769      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48770          * 
48771          *}
48772      */
48773     xgrid : false, 
48774     /**
48775      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48776      * {tag: "input", type: "checkbox", autocomplete: "off"})
48777      */
48778    // defaultAutoCreate : { tag: 'div' },
48779     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48780     /**
48781      * @cfg {String} addTitle Text to include for adding a title.
48782      */
48783     addTitle : false,
48784     //
48785     onResize : function(){
48786         Roo.form.Field.superclass.onResize.apply(this, arguments);
48787     },
48788
48789     initEvents : function(){
48790         // Roo.form.Checkbox.superclass.initEvents.call(this);
48791         // has no events...
48792        
48793     },
48794
48795
48796     getResizeEl : function(){
48797         return this.wrap;
48798     },
48799
48800     getPositionEl : function(){
48801         return this.wrap;
48802     },
48803
48804     // private
48805     onRender : function(ct, position){
48806         
48807         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48808         var style = this.style;
48809         delete this.style;
48810         
48811         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48812         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48813         this.viewEl = this.wrap.createChild({ tag: 'div' });
48814         if (style) {
48815             this.viewEl.applyStyles(style);
48816         }
48817         if (this.width) {
48818             this.viewEl.setWidth(this.width);
48819         }
48820         if (this.height) {
48821             this.viewEl.setHeight(this.height);
48822         }
48823         //if(this.inputValue !== undefined){
48824         //this.setValue(this.value);
48825         
48826         
48827         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48828         
48829         
48830         this.grid.render();
48831         this.grid.getDataSource().on('remove', this.refreshValue, this);
48832         this.grid.getDataSource().on('update', this.refreshValue, this);
48833         this.grid.on('afteredit', this.refreshValue, this);
48834  
48835     },
48836      
48837     
48838     /**
48839      * Sets the value of the item. 
48840      * @param {String} either an object  or a string..
48841      */
48842     setValue : function(v){
48843         //this.value = v;
48844         v = v || []; // empty set..
48845         // this does not seem smart - it really only affects memoryproxy grids..
48846         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48847             var ds = this.grid.getDataSource();
48848             // assumes a json reader..
48849             var data = {}
48850             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48851             ds.loadData( data);
48852         }
48853         // clear selection so it does not get stale.
48854         if (this.grid.sm) { 
48855             this.grid.sm.clearSelections();
48856         }
48857         
48858         Roo.form.GridField.superclass.setValue.call(this, v);
48859         this.refreshValue();
48860         // should load data in the grid really....
48861     },
48862     
48863     // private
48864     refreshValue: function() {
48865          var val = [];
48866         this.grid.getDataSource().each(function(r) {
48867             val.push(r.data);
48868         });
48869         this.el.dom.value = Roo.encode(val);
48870     }
48871     
48872      
48873     
48874     
48875 });/*
48876  * Based on:
48877  * Ext JS Library 1.1.1
48878  * Copyright(c) 2006-2007, Ext JS, LLC.
48879  *
48880  * Originally Released Under LGPL - original licence link has changed is not relivant.
48881  *
48882  * Fork - LGPL
48883  * <script type="text/javascript">
48884  */
48885 /**
48886  * @class Roo.form.DisplayField
48887  * @extends Roo.form.Field
48888  * A generic Field to display non-editable data.
48889  * @cfg {Boolean} closable (true|false) default false
48890  * @constructor
48891  * Creates a new Display Field item.
48892  * @param {Object} config Configuration options
48893  */
48894 Roo.form.DisplayField = function(config){
48895     Roo.form.DisplayField.superclass.constructor.call(this, config);
48896     
48897     this.addEvents({
48898         /**
48899          * @event close
48900          * Fires after the click the close btn
48901              * @param {Roo.form.DisplayField} this
48902              */
48903         close : true
48904     });
48905 };
48906
48907 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
48908     inputType:      'hidden',
48909     allowBlank:     true,
48910     readOnly:         true,
48911     
48912  
48913     /**
48914      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48915      */
48916     focusClass : undefined,
48917     /**
48918      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48919      */
48920     fieldClass: 'x-form-field',
48921     
48922      /**
48923      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
48924      */
48925     valueRenderer: undefined,
48926     
48927     width: 100,
48928     /**
48929      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48930      * {tag: "input", type: "checkbox", autocomplete: "off"})
48931      */
48932      
48933  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
48934  
48935     closable : false,
48936     
48937     onResize : function(){
48938         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
48939         
48940     },
48941
48942     initEvents : function(){
48943         // Roo.form.Checkbox.superclass.initEvents.call(this);
48944         // has no events...
48945         
48946         if(this.closable){
48947             this.closeEl.on('click', this.onClose, this);
48948         }
48949        
48950     },
48951
48952
48953     getResizeEl : function(){
48954         return this.wrap;
48955     },
48956
48957     getPositionEl : function(){
48958         return this.wrap;
48959     },
48960
48961     // private
48962     onRender : function(ct, position){
48963         
48964         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
48965         //if(this.inputValue !== undefined){
48966         this.wrap = this.el.wrap();
48967         
48968         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
48969         
48970         if(this.closable){
48971             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
48972         }
48973         
48974         if (this.bodyStyle) {
48975             this.viewEl.applyStyles(this.bodyStyle);
48976         }
48977         //this.viewEl.setStyle('padding', '2px');
48978         
48979         this.setValue(this.value);
48980         
48981     },
48982 /*
48983     // private
48984     initValue : Roo.emptyFn,
48985
48986   */
48987
48988         // private
48989     onClick : function(){
48990         
48991     },
48992
48993     /**
48994      * Sets the checked state of the checkbox.
48995      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
48996      */
48997     setValue : function(v){
48998         this.value = v;
48999         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
49000         // this might be called before we have a dom element..
49001         if (!this.viewEl) {
49002             return;
49003         }
49004         this.viewEl.dom.innerHTML = html;
49005         Roo.form.DisplayField.superclass.setValue.call(this, v);
49006
49007     },
49008     
49009     onClose : function(e)
49010     {
49011         e.preventDefault();
49012         
49013         this.fireEvent('close', this);
49014     }
49015 });/*
49016  * 
49017  * Licence- LGPL
49018  * 
49019  */
49020
49021 /**
49022  * @class Roo.form.DayPicker
49023  * @extends Roo.form.Field
49024  * A Day picker show [M] [T] [W] ....
49025  * @constructor
49026  * Creates a new Day Picker
49027  * @param {Object} config Configuration options
49028  */
49029 Roo.form.DayPicker= function(config){
49030     Roo.form.DayPicker.superclass.constructor.call(this, config);
49031      
49032 };
49033
49034 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49035     /**
49036      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49037      */
49038     focusClass : undefined,
49039     /**
49040      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49041      */
49042     fieldClass: "x-form-field",
49043    
49044     /**
49045      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49046      * {tag: "input", type: "checkbox", autocomplete: "off"})
49047      */
49048     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49049     
49050    
49051     actionMode : 'viewEl', 
49052     //
49053     // private
49054  
49055     inputType : 'hidden',
49056     
49057      
49058     inputElement: false, // real input element?
49059     basedOn: false, // ????
49060     
49061     isFormField: true, // not sure where this is needed!!!!
49062
49063     onResize : function(){
49064         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49065         if(!this.boxLabel){
49066             this.el.alignTo(this.wrap, 'c-c');
49067         }
49068     },
49069
49070     initEvents : function(){
49071         Roo.form.Checkbox.superclass.initEvents.call(this);
49072         this.el.on("click", this.onClick,  this);
49073         this.el.on("change", this.onClick,  this);
49074     },
49075
49076
49077     getResizeEl : function(){
49078         return this.wrap;
49079     },
49080
49081     getPositionEl : function(){
49082         return this.wrap;
49083     },
49084
49085     
49086     // private
49087     onRender : function(ct, position){
49088         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49089        
49090         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49091         
49092         var r1 = '<table><tr>';
49093         var r2 = '<tr class="x-form-daypick-icons">';
49094         for (var i=0; i < 7; i++) {
49095             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49096             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49097         }
49098         
49099         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49100         viewEl.select('img').on('click', this.onClick, this);
49101         this.viewEl = viewEl;   
49102         
49103         
49104         // this will not work on Chrome!!!
49105         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49106         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49107         
49108         
49109           
49110
49111     },
49112
49113     // private
49114     initValue : Roo.emptyFn,
49115
49116     /**
49117      * Returns the checked state of the checkbox.
49118      * @return {Boolean} True if checked, else false
49119      */
49120     getValue : function(){
49121         return this.el.dom.value;
49122         
49123     },
49124
49125         // private
49126     onClick : function(e){ 
49127         //this.setChecked(!this.checked);
49128         Roo.get(e.target).toggleClass('x-menu-item-checked');
49129         this.refreshValue();
49130         //if(this.el.dom.checked != this.checked){
49131         //    this.setValue(this.el.dom.checked);
49132        // }
49133     },
49134     
49135     // private
49136     refreshValue : function()
49137     {
49138         var val = '';
49139         this.viewEl.select('img',true).each(function(e,i,n)  {
49140             val += e.is(".x-menu-item-checked") ? String(n) : '';
49141         });
49142         this.setValue(val, true);
49143     },
49144
49145     /**
49146      * Sets the checked state of the checkbox.
49147      * On is always based on a string comparison between inputValue and the param.
49148      * @param {Boolean/String} value - the value to set 
49149      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49150      */
49151     setValue : function(v,suppressEvent){
49152         if (!this.el.dom) {
49153             return;
49154         }
49155         var old = this.el.dom.value ;
49156         this.el.dom.value = v;
49157         if (suppressEvent) {
49158             return ;
49159         }
49160          
49161         // update display..
49162         this.viewEl.select('img',true).each(function(e,i,n)  {
49163             
49164             var on = e.is(".x-menu-item-checked");
49165             var newv = v.indexOf(String(n)) > -1;
49166             if (on != newv) {
49167                 e.toggleClass('x-menu-item-checked');
49168             }
49169             
49170         });
49171         
49172         
49173         this.fireEvent('change', this, v, old);
49174         
49175         
49176     },
49177    
49178     // handle setting of hidden value by some other method!!?!?
49179     setFromHidden: function()
49180     {
49181         if(!this.el){
49182             return;
49183         }
49184         //console.log("SET FROM HIDDEN");
49185         //alert('setFrom hidden');
49186         this.setValue(this.el.dom.value);
49187     },
49188     
49189     onDestroy : function()
49190     {
49191         if(this.viewEl){
49192             Roo.get(this.viewEl).remove();
49193         }
49194          
49195         Roo.form.DayPicker.superclass.onDestroy.call(this);
49196     }
49197
49198 });/*
49199  * RooJS Library 1.1.1
49200  * Copyright(c) 2008-2011  Alan Knowles
49201  *
49202  * License - LGPL
49203  */
49204  
49205
49206 /**
49207  * @class Roo.form.ComboCheck
49208  * @extends Roo.form.ComboBox
49209  * A combobox for multiple select items.
49210  *
49211  * FIXME - could do with a reset button..
49212  * 
49213  * @constructor
49214  * Create a new ComboCheck
49215  * @param {Object} config Configuration options
49216  */
49217 Roo.form.ComboCheck = function(config){
49218     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49219     // should verify some data...
49220     // like
49221     // hiddenName = required..
49222     // displayField = required
49223     // valudField == required
49224     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49225     var _t = this;
49226     Roo.each(req, function(e) {
49227         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49228             throw "Roo.form.ComboCheck : missing value for: " + e;
49229         }
49230     });
49231     
49232     
49233 };
49234
49235 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49236      
49237      
49238     editable : false,
49239      
49240     selectedClass: 'x-menu-item-checked', 
49241     
49242     // private
49243     onRender : function(ct, position){
49244         var _t = this;
49245         
49246         
49247         
49248         if(!this.tpl){
49249             var cls = 'x-combo-list';
49250
49251             
49252             this.tpl =  new Roo.Template({
49253                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49254                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49255                    '<span>{' + this.displayField + '}</span>' +
49256                     '</div>' 
49257                 
49258             });
49259         }
49260  
49261         
49262         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49263         this.view.singleSelect = false;
49264         this.view.multiSelect = true;
49265         this.view.toggleSelect = true;
49266         this.pageTb.add(new Roo.Toolbar.Fill(), {
49267             
49268             text: 'Done',
49269             handler: function()
49270             {
49271                 _t.collapse();
49272             }
49273         });
49274     },
49275     
49276     onViewOver : function(e, t){
49277         // do nothing...
49278         return;
49279         
49280     },
49281     
49282     onViewClick : function(doFocus,index){
49283         return;
49284         
49285     },
49286     select: function () {
49287         //Roo.log("SELECT CALLED");
49288     },
49289      
49290     selectByValue : function(xv, scrollIntoView){
49291         var ar = this.getValueArray();
49292         var sels = [];
49293         
49294         Roo.each(ar, function(v) {
49295             if(v === undefined || v === null){
49296                 return;
49297             }
49298             var r = this.findRecord(this.valueField, v);
49299             if(r){
49300                 sels.push(this.store.indexOf(r))
49301                 
49302             }
49303         },this);
49304         this.view.select(sels);
49305         return false;
49306     },
49307     
49308     
49309     
49310     onSelect : function(record, index){
49311        // Roo.log("onselect Called");
49312        // this is only called by the clear button now..
49313         this.view.clearSelections();
49314         this.setValue('[]');
49315         if (this.value != this.valueBefore) {
49316             this.fireEvent('change', this, this.value, this.valueBefore);
49317             this.valueBefore = this.value;
49318         }
49319     },
49320     getValueArray : function()
49321     {
49322         var ar = [] ;
49323         
49324         try {
49325             //Roo.log(this.value);
49326             if (typeof(this.value) == 'undefined') {
49327                 return [];
49328             }
49329             var ar = Roo.decode(this.value);
49330             return  ar instanceof Array ? ar : []; //?? valid?
49331             
49332         } catch(e) {
49333             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49334             return [];
49335         }
49336          
49337     },
49338     expand : function ()
49339     {
49340         
49341         Roo.form.ComboCheck.superclass.expand.call(this);
49342         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49343         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49344         
49345
49346     },
49347     
49348     collapse : function(){
49349         Roo.form.ComboCheck.superclass.collapse.call(this);
49350         var sl = this.view.getSelectedIndexes();
49351         var st = this.store;
49352         var nv = [];
49353         var tv = [];
49354         var r;
49355         Roo.each(sl, function(i) {
49356             r = st.getAt(i);
49357             nv.push(r.get(this.valueField));
49358         },this);
49359         this.setValue(Roo.encode(nv));
49360         if (this.value != this.valueBefore) {
49361
49362             this.fireEvent('change', this, this.value, this.valueBefore);
49363             this.valueBefore = this.value;
49364         }
49365         
49366     },
49367     
49368     setValue : function(v){
49369         // Roo.log(v);
49370         this.value = v;
49371         
49372         var vals = this.getValueArray();
49373         var tv = [];
49374         Roo.each(vals, function(k) {
49375             var r = this.findRecord(this.valueField, k);
49376             if(r){
49377                 tv.push(r.data[this.displayField]);
49378             }else if(this.valueNotFoundText !== undefined){
49379                 tv.push( this.valueNotFoundText );
49380             }
49381         },this);
49382        // Roo.log(tv);
49383         
49384         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49385         this.hiddenField.value = v;
49386         this.value = v;
49387     }
49388     
49389 });/*
49390  * Based on:
49391  * Ext JS Library 1.1.1
49392  * Copyright(c) 2006-2007, Ext JS, LLC.
49393  *
49394  * Originally Released Under LGPL - original licence link has changed is not relivant.
49395  *
49396  * Fork - LGPL
49397  * <script type="text/javascript">
49398  */
49399  
49400 /**
49401  * @class Roo.form.Signature
49402  * @extends Roo.form.Field
49403  * Signature field.  
49404  * @constructor
49405  * 
49406  * @param {Object} config Configuration options
49407  */
49408
49409 Roo.form.Signature = function(config){
49410     Roo.form.Signature.superclass.constructor.call(this, config);
49411     
49412     this.addEvents({// not in used??
49413          /**
49414          * @event confirm
49415          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49416              * @param {Roo.form.Signature} combo This combo box
49417              */
49418         'confirm' : true,
49419         /**
49420          * @event reset
49421          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49422              * @param {Roo.form.ComboBox} combo This combo box
49423              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49424              */
49425         'reset' : true
49426     });
49427 };
49428
49429 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49430     /**
49431      * @cfg {Object} labels Label to use when rendering a form.
49432      * defaults to 
49433      * labels : { 
49434      *      clear : "Clear",
49435      *      confirm : "Confirm"
49436      *  }
49437      */
49438     labels : { 
49439         clear : "Clear",
49440         confirm : "Confirm"
49441     },
49442     /**
49443      * @cfg {Number} width The signature panel width (defaults to 300)
49444      */
49445     width: 300,
49446     /**
49447      * @cfg {Number} height The signature panel height (defaults to 100)
49448      */
49449     height : 100,
49450     /**
49451      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49452      */
49453     allowBlank : false,
49454     
49455     //private
49456     // {Object} signPanel The signature SVG panel element (defaults to {})
49457     signPanel : {},
49458     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49459     isMouseDown : false,
49460     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49461     isConfirmed : false,
49462     // {String} signatureTmp SVG mapping string (defaults to empty string)
49463     signatureTmp : '',
49464     
49465     
49466     defaultAutoCreate : { // modified by initCompnoent..
49467         tag: "input",
49468         type:"hidden"
49469     },
49470
49471     // private
49472     onRender : function(ct, position){
49473         
49474         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49475         
49476         this.wrap = this.el.wrap({
49477             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49478         });
49479         
49480         this.createToolbar(this);
49481         this.signPanel = this.wrap.createChild({
49482                 tag: 'div',
49483                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49484             }, this.el
49485         );
49486             
49487         this.svgID = Roo.id();
49488         this.svgEl = this.signPanel.createChild({
49489               xmlns : 'http://www.w3.org/2000/svg',
49490               tag : 'svg',
49491               id : this.svgID + "-svg",
49492               width: this.width,
49493               height: this.height,
49494               viewBox: '0 0 '+this.width+' '+this.height,
49495               cn : [
49496                 {
49497                     tag: "rect",
49498                     id: this.svgID + "-svg-r",
49499                     width: this.width,
49500                     height: this.height,
49501                     fill: "#ffa"
49502                 },
49503                 {
49504                     tag: "line",
49505                     id: this.svgID + "-svg-l",
49506                     x1: "0", // start
49507                     y1: (this.height*0.8), // start set the line in 80% of height
49508                     x2: this.width, // end
49509                     y2: (this.height*0.8), // end set the line in 80% of height
49510                     'stroke': "#666",
49511                     'stroke-width': "1",
49512                     'stroke-dasharray': "3",
49513                     'shape-rendering': "crispEdges",
49514                     'pointer-events': "none"
49515                 },
49516                 {
49517                     tag: "path",
49518                     id: this.svgID + "-svg-p",
49519                     'stroke': "navy",
49520                     'stroke-width': "3",
49521                     'fill': "none",
49522                     'pointer-events': 'none'
49523                 }
49524               ]
49525         });
49526         this.createSVG();
49527         this.svgBox = this.svgEl.dom.getScreenCTM();
49528     },
49529     createSVG : function(){ 
49530         var svg = this.signPanel;
49531         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49532         var t = this;
49533
49534         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49535         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49536         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49537         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49538         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49539         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49540         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49541         
49542     },
49543     isTouchEvent : function(e){
49544         return e.type.match(/^touch/);
49545     },
49546     getCoords : function (e) {
49547         var pt    = this.svgEl.dom.createSVGPoint();
49548         pt.x = e.clientX; 
49549         pt.y = e.clientY;
49550         if (this.isTouchEvent(e)) {
49551             pt.x =  e.targetTouches[0].clientX;
49552             pt.y = e.targetTouches[0].clientY;
49553         }
49554         var a = this.svgEl.dom.getScreenCTM();
49555         var b = a.inverse();
49556         var mx = pt.matrixTransform(b);
49557         return mx.x + ',' + mx.y;
49558     },
49559     //mouse event headler 
49560     down : function (e) {
49561         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49562         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49563         
49564         this.isMouseDown = true;
49565         
49566         e.preventDefault();
49567     },
49568     move : function (e) {
49569         if (this.isMouseDown) {
49570             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49571             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49572         }
49573         
49574         e.preventDefault();
49575     },
49576     up : function (e) {
49577         this.isMouseDown = false;
49578         var sp = this.signatureTmp.split(' ');
49579         
49580         if(sp.length > 1){
49581             if(!sp[sp.length-2].match(/^L/)){
49582                 sp.pop();
49583                 sp.pop();
49584                 sp.push("");
49585                 this.signatureTmp = sp.join(" ");
49586             }
49587         }
49588         if(this.getValue() != this.signatureTmp){
49589             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49590             this.isConfirmed = false;
49591         }
49592         e.preventDefault();
49593     },
49594     
49595     /**
49596      * Protected method that will not generally be called directly. It
49597      * is called when the editor creates its toolbar. Override this method if you need to
49598      * add custom toolbar buttons.
49599      * @param {HtmlEditor} editor
49600      */
49601     createToolbar : function(editor){
49602          function btn(id, toggle, handler){
49603             var xid = fid + '-'+ id ;
49604             return {
49605                 id : xid,
49606                 cmd : id,
49607                 cls : 'x-btn-icon x-edit-'+id,
49608                 enableToggle:toggle !== false,
49609                 scope: editor, // was editor...
49610                 handler:handler||editor.relayBtnCmd,
49611                 clickEvent:'mousedown',
49612                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49613                 tabIndex:-1
49614             };
49615         }
49616         
49617         
49618         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49619         this.tb = tb;
49620         this.tb.add(
49621            {
49622                 cls : ' x-signature-btn x-signature-'+id,
49623                 scope: editor, // was editor...
49624                 handler: this.reset,
49625                 clickEvent:'mousedown',
49626                 text: this.labels.clear
49627             },
49628             {
49629                  xtype : 'Fill',
49630                  xns: Roo.Toolbar
49631             }, 
49632             {
49633                 cls : '  x-signature-btn x-signature-'+id,
49634                 scope: editor, // was editor...
49635                 handler: this.confirmHandler,
49636                 clickEvent:'mousedown',
49637                 text: this.labels.confirm
49638             }
49639         );
49640     
49641     },
49642     //public
49643     /**
49644      * when user is clicked confirm then show this image.....
49645      * 
49646      * @return {String} Image Data URI
49647      */
49648     getImageDataURI : function(){
49649         var svg = this.svgEl.dom.parentNode.innerHTML;
49650         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49651         return src; 
49652     },
49653     /**
49654      * 
49655      * @return {Boolean} this.isConfirmed
49656      */
49657     getConfirmed : function(){
49658         return this.isConfirmed;
49659     },
49660     /**
49661      * 
49662      * @return {Number} this.width
49663      */
49664     getWidth : function(){
49665         return this.width;
49666     },
49667     /**
49668      * 
49669      * @return {Number} this.height
49670      */
49671     getHeight : function(){
49672         return this.height;
49673     },
49674     // private
49675     getSignature : function(){
49676         return this.signatureTmp;
49677     },
49678     // private
49679     reset : function(){
49680         this.signatureTmp = '';
49681         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49682         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49683         this.isConfirmed = false;
49684         Roo.form.Signature.superclass.reset.call(this);
49685     },
49686     setSignature : function(s){
49687         this.signatureTmp = s;
49688         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49689         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49690         this.setValue(s);
49691         this.isConfirmed = false;
49692         Roo.form.Signature.superclass.reset.call(this);
49693     }, 
49694     test : function(){
49695 //        Roo.log(this.signPanel.dom.contentWindow.up())
49696     },
49697     //private
49698     setConfirmed : function(){
49699         
49700         
49701         
49702 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49703     },
49704     // private
49705     confirmHandler : function(){
49706         if(!this.getSignature()){
49707             return;
49708         }
49709         
49710         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49711         this.setValue(this.getSignature());
49712         this.isConfirmed = true;
49713         
49714         this.fireEvent('confirm', this);
49715     },
49716     // private
49717     // Subclasses should provide the validation implementation by overriding this
49718     validateValue : function(value){
49719         if(this.allowBlank){
49720             return true;
49721         }
49722         
49723         if(this.isConfirmed){
49724             return true;
49725         }
49726         return false;
49727     }
49728 });/*
49729  * Based on:
49730  * Ext JS Library 1.1.1
49731  * Copyright(c) 2006-2007, Ext JS, LLC.
49732  *
49733  * Originally Released Under LGPL - original licence link has changed is not relivant.
49734  *
49735  * Fork - LGPL
49736  * <script type="text/javascript">
49737  */
49738  
49739
49740 /**
49741  * @class Roo.form.ComboBox
49742  * @extends Roo.form.TriggerField
49743  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49744  * @constructor
49745  * Create a new ComboBox.
49746  * @param {Object} config Configuration options
49747  */
49748 Roo.form.Select = function(config){
49749     Roo.form.Select.superclass.constructor.call(this, config);
49750      
49751 };
49752
49753 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49754     /**
49755      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49756      */
49757     /**
49758      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49759      * rendering into an Roo.Editor, defaults to false)
49760      */
49761     /**
49762      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49763      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49764      */
49765     /**
49766      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49767      */
49768     /**
49769      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49770      * the dropdown list (defaults to undefined, with no header element)
49771      */
49772
49773      /**
49774      * @cfg {String/Roo.Template} tpl The template to use to render the output
49775      */
49776      
49777     // private
49778     defaultAutoCreate : {tag: "select"  },
49779     /**
49780      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49781      */
49782     listWidth: undefined,
49783     /**
49784      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49785      * mode = 'remote' or 'text' if mode = 'local')
49786      */
49787     displayField: undefined,
49788     /**
49789      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49790      * mode = 'remote' or 'value' if mode = 'local'). 
49791      * Note: use of a valueField requires the user make a selection
49792      * in order for a value to be mapped.
49793      */
49794     valueField: undefined,
49795     
49796     
49797     /**
49798      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49799      * field's data value (defaults to the underlying DOM element's name)
49800      */
49801     hiddenName: undefined,
49802     /**
49803      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49804      */
49805     listClass: '',
49806     /**
49807      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49808      */
49809     selectedClass: 'x-combo-selected',
49810     /**
49811      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49812      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49813      * which displays a downward arrow icon).
49814      */
49815     triggerClass : 'x-form-arrow-trigger',
49816     /**
49817      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49818      */
49819     shadow:'sides',
49820     /**
49821      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49822      * anchor positions (defaults to 'tl-bl')
49823      */
49824     listAlign: 'tl-bl?',
49825     /**
49826      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49827      */
49828     maxHeight: 300,
49829     /**
49830      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49831      * query specified by the allQuery config option (defaults to 'query')
49832      */
49833     triggerAction: 'query',
49834     /**
49835      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49836      * (defaults to 4, does not apply if editable = false)
49837      */
49838     minChars : 4,
49839     /**
49840      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49841      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49842      */
49843     typeAhead: false,
49844     /**
49845      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49846      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49847      */
49848     queryDelay: 500,
49849     /**
49850      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49851      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49852      */
49853     pageSize: 0,
49854     /**
49855      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49856      * when editable = true (defaults to false)
49857      */
49858     selectOnFocus:false,
49859     /**
49860      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
49861      */
49862     queryParam: 'query',
49863     /**
49864      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
49865      * when mode = 'remote' (defaults to 'Loading...')
49866      */
49867     loadingText: 'Loading...',
49868     /**
49869      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
49870      */
49871     resizable: false,
49872     /**
49873      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
49874      */
49875     handleHeight : 8,
49876     /**
49877      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
49878      * traditional select (defaults to true)
49879      */
49880     editable: true,
49881     /**
49882      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
49883      */
49884     allQuery: '',
49885     /**
49886      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
49887      */
49888     mode: 'remote',
49889     /**
49890      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
49891      * listWidth has a higher value)
49892      */
49893     minListWidth : 70,
49894     /**
49895      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
49896      * allow the user to set arbitrary text into the field (defaults to false)
49897      */
49898     forceSelection:false,
49899     /**
49900      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
49901      * if typeAhead = true (defaults to 250)
49902      */
49903     typeAheadDelay : 250,
49904     /**
49905      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
49906      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
49907      */
49908     valueNotFoundText : undefined,
49909     
49910     /**
49911      * @cfg {String} defaultValue The value displayed after loading the store.
49912      */
49913     defaultValue: '',
49914     
49915     /**
49916      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
49917      */
49918     blockFocus : false,
49919     
49920     /**
49921      * @cfg {Boolean} disableClear Disable showing of clear button.
49922      */
49923     disableClear : false,
49924     /**
49925      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
49926      */
49927     alwaysQuery : false,
49928     
49929     //private
49930     addicon : false,
49931     editicon: false,
49932     
49933     // element that contains real text value.. (when hidden is used..)
49934      
49935     // private
49936     onRender : function(ct, position){
49937         Roo.form.Field.prototype.onRender.call(this, ct, position);
49938         
49939         if(this.store){
49940             this.store.on('beforeload', this.onBeforeLoad, this);
49941             this.store.on('load', this.onLoad, this);
49942             this.store.on('loadexception', this.onLoadException, this);
49943             this.store.load({});
49944         }
49945         
49946         
49947         
49948     },
49949
49950     // private
49951     initEvents : function(){
49952         //Roo.form.ComboBox.superclass.initEvents.call(this);
49953  
49954     },
49955
49956     onDestroy : function(){
49957        
49958         if(this.store){
49959             this.store.un('beforeload', this.onBeforeLoad, this);
49960             this.store.un('load', this.onLoad, this);
49961             this.store.un('loadexception', this.onLoadException, this);
49962         }
49963         //Roo.form.ComboBox.superclass.onDestroy.call(this);
49964     },
49965
49966     // private
49967     fireKey : function(e){
49968         if(e.isNavKeyPress() && !this.list.isVisible()){
49969             this.fireEvent("specialkey", this, e);
49970         }
49971     },
49972
49973     // private
49974     onResize: function(w, h){
49975         
49976         return; 
49977     
49978         
49979     },
49980
49981     /**
49982      * Allow or prevent the user from directly editing the field text.  If false is passed,
49983      * the user will only be able to select from the items defined in the dropdown list.  This method
49984      * is the runtime equivalent of setting the 'editable' config option at config time.
49985      * @param {Boolean} value True to allow the user to directly edit the field text
49986      */
49987     setEditable : function(value){
49988          
49989     },
49990
49991     // private
49992     onBeforeLoad : function(){
49993         
49994         Roo.log("Select before load");
49995         return;
49996     
49997         this.innerList.update(this.loadingText ?
49998                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
49999         //this.restrictHeight();
50000         this.selectedIndex = -1;
50001     },
50002
50003     // private
50004     onLoad : function(){
50005
50006     
50007         var dom = this.el.dom;
50008         dom.innerHTML = '';
50009          var od = dom.ownerDocument;
50010          
50011         if (this.emptyText) {
50012             var op = od.createElement('option');
50013             op.setAttribute('value', '');
50014             op.innerHTML = String.format('{0}', this.emptyText);
50015             dom.appendChild(op);
50016         }
50017         if(this.store.getCount() > 0){
50018            
50019             var vf = this.valueField;
50020             var df = this.displayField;
50021             this.store.data.each(function(r) {
50022                 // which colmsn to use... testing - cdoe / title..
50023                 var op = od.createElement('option');
50024                 op.setAttribute('value', r.data[vf]);
50025                 op.innerHTML = String.format('{0}', r.data[df]);
50026                 dom.appendChild(op);
50027             });
50028             if (typeof(this.defaultValue != 'undefined')) {
50029                 this.setValue(this.defaultValue);
50030             }
50031             
50032              
50033         }else{
50034             //this.onEmptyResults();
50035         }
50036         //this.el.focus();
50037     },
50038     // private
50039     onLoadException : function()
50040     {
50041         dom.innerHTML = '';
50042             
50043         Roo.log("Select on load exception");
50044         return;
50045     
50046         this.collapse();
50047         Roo.log(this.store.reader.jsonData);
50048         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50049             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50050         }
50051         
50052         
50053     },
50054     // private
50055     onTypeAhead : function(){
50056          
50057     },
50058
50059     // private
50060     onSelect : function(record, index){
50061         Roo.log('on select?');
50062         return;
50063         if(this.fireEvent('beforeselect', this, record, index) !== false){
50064             this.setFromData(index > -1 ? record.data : false);
50065             this.collapse();
50066             this.fireEvent('select', this, record, index);
50067         }
50068     },
50069
50070     /**
50071      * Returns the currently selected field value or empty string if no value is set.
50072      * @return {String} value The selected value
50073      */
50074     getValue : function(){
50075         var dom = this.el.dom;
50076         this.value = dom.options[dom.selectedIndex].value;
50077         return this.value;
50078         
50079     },
50080
50081     /**
50082      * Clears any text/value currently set in the field
50083      */
50084     clearValue : function(){
50085         this.value = '';
50086         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50087         
50088     },
50089
50090     /**
50091      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50092      * will be displayed in the field.  If the value does not match the data value of an existing item,
50093      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50094      * Otherwise the field will be blank (although the value will still be set).
50095      * @param {String} value The value to match
50096      */
50097     setValue : function(v){
50098         var d = this.el.dom;
50099         for (var i =0; i < d.options.length;i++) {
50100             if (v == d.options[i].value) {
50101                 d.selectedIndex = i;
50102                 this.value = v;
50103                 return;
50104             }
50105         }
50106         this.clearValue();
50107     },
50108     /**
50109      * @property {Object} the last set data for the element
50110      */
50111     
50112     lastData : false,
50113     /**
50114      * Sets the value of the field based on a object which is related to the record format for the store.
50115      * @param {Object} value the value to set as. or false on reset?
50116      */
50117     setFromData : function(o){
50118         Roo.log('setfrom data?');
50119          
50120         
50121         
50122     },
50123     // private
50124     reset : function(){
50125         this.clearValue();
50126     },
50127     // private
50128     findRecord : function(prop, value){
50129         
50130         return false;
50131     
50132         var record;
50133         if(this.store.getCount() > 0){
50134             this.store.each(function(r){
50135                 if(r.data[prop] == value){
50136                     record = r;
50137                     return false;
50138                 }
50139                 return true;
50140             });
50141         }
50142         return record;
50143     },
50144     
50145     getName: function()
50146     {
50147         // returns hidden if it's set..
50148         if (!this.rendered) {return ''};
50149         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50150         
50151     },
50152      
50153
50154     
50155
50156     // private
50157     onEmptyResults : function(){
50158         Roo.log('empty results');
50159         //this.collapse();
50160     },
50161
50162     /**
50163      * Returns true if the dropdown list is expanded, else false.
50164      */
50165     isExpanded : function(){
50166         return false;
50167     },
50168
50169     /**
50170      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50171      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50172      * @param {String} value The data value of the item to select
50173      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50174      * selected item if it is not currently in view (defaults to true)
50175      * @return {Boolean} True if the value matched an item in the list, else false
50176      */
50177     selectByValue : function(v, scrollIntoView){
50178         Roo.log('select By Value');
50179         return false;
50180     
50181         if(v !== undefined && v !== null){
50182             var r = this.findRecord(this.valueField || this.displayField, v);
50183             if(r){
50184                 this.select(this.store.indexOf(r), scrollIntoView);
50185                 return true;
50186             }
50187         }
50188         return false;
50189     },
50190
50191     /**
50192      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50193      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50194      * @param {Number} index The zero-based index of the list item to select
50195      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50196      * selected item if it is not currently in view (defaults to true)
50197      */
50198     select : function(index, scrollIntoView){
50199         Roo.log('select ');
50200         return  ;
50201         
50202         this.selectedIndex = index;
50203         this.view.select(index);
50204         if(scrollIntoView !== false){
50205             var el = this.view.getNode(index);
50206             if(el){
50207                 this.innerList.scrollChildIntoView(el, false);
50208             }
50209         }
50210     },
50211
50212       
50213
50214     // private
50215     validateBlur : function(){
50216         
50217         return;
50218         
50219     },
50220
50221     // private
50222     initQuery : function(){
50223         this.doQuery(this.getRawValue());
50224     },
50225
50226     // private
50227     doForce : function(){
50228         if(this.el.dom.value.length > 0){
50229             this.el.dom.value =
50230                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50231              
50232         }
50233     },
50234
50235     /**
50236      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50237      * query allowing the query action to be canceled if needed.
50238      * @param {String} query The SQL query to execute
50239      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50240      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50241      * saved in the current store (defaults to false)
50242      */
50243     doQuery : function(q, forceAll){
50244         
50245         Roo.log('doQuery?');
50246         if(q === undefined || q === null){
50247             q = '';
50248         }
50249         var qe = {
50250             query: q,
50251             forceAll: forceAll,
50252             combo: this,
50253             cancel:false
50254         };
50255         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50256             return false;
50257         }
50258         q = qe.query;
50259         forceAll = qe.forceAll;
50260         if(forceAll === true || (q.length >= this.minChars)){
50261             if(this.lastQuery != q || this.alwaysQuery){
50262                 this.lastQuery = q;
50263                 if(this.mode == 'local'){
50264                     this.selectedIndex = -1;
50265                     if(forceAll){
50266                         this.store.clearFilter();
50267                     }else{
50268                         this.store.filter(this.displayField, q);
50269                     }
50270                     this.onLoad();
50271                 }else{
50272                     this.store.baseParams[this.queryParam] = q;
50273                     this.store.load({
50274                         params: this.getParams(q)
50275                     });
50276                     this.expand();
50277                 }
50278             }else{
50279                 this.selectedIndex = -1;
50280                 this.onLoad();   
50281             }
50282         }
50283     },
50284
50285     // private
50286     getParams : function(q){
50287         var p = {};
50288         //p[this.queryParam] = q;
50289         if(this.pageSize){
50290             p.start = 0;
50291             p.limit = this.pageSize;
50292         }
50293         return p;
50294     },
50295
50296     /**
50297      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50298      */
50299     collapse : function(){
50300         
50301     },
50302
50303     // private
50304     collapseIf : function(e){
50305         
50306     },
50307
50308     /**
50309      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50310      */
50311     expand : function(){
50312         
50313     } ,
50314
50315     // private
50316      
50317
50318     /** 
50319     * @cfg {Boolean} grow 
50320     * @hide 
50321     */
50322     /** 
50323     * @cfg {Number} growMin 
50324     * @hide 
50325     */
50326     /** 
50327     * @cfg {Number} growMax 
50328     * @hide 
50329     */
50330     /**
50331      * @hide
50332      * @method autoSize
50333      */
50334     
50335     setWidth : function()
50336     {
50337         
50338     },
50339     getResizeEl : function(){
50340         return this.el;
50341     }
50342 });//<script type="text/javasscript">
50343  
50344
50345 /**
50346  * @class Roo.DDView
50347  * A DnD enabled version of Roo.View.
50348  * @param {Element/String} container The Element in which to create the View.
50349  * @param {String} tpl The template string used to create the markup for each element of the View
50350  * @param {Object} config The configuration properties. These include all the config options of
50351  * {@link Roo.View} plus some specific to this class.<br>
50352  * <p>
50353  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50354  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50355  * <p>
50356  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50357 .x-view-drag-insert-above {
50358         border-top:1px dotted #3366cc;
50359 }
50360 .x-view-drag-insert-below {
50361         border-bottom:1px dotted #3366cc;
50362 }
50363 </code></pre>
50364  * 
50365  */
50366  
50367 Roo.DDView = function(container, tpl, config) {
50368     Roo.DDView.superclass.constructor.apply(this, arguments);
50369     this.getEl().setStyle("outline", "0px none");
50370     this.getEl().unselectable();
50371     if (this.dragGroup) {
50372                 this.setDraggable(this.dragGroup.split(","));
50373     }
50374     if (this.dropGroup) {
50375                 this.setDroppable(this.dropGroup.split(","));
50376     }
50377     if (this.deletable) {
50378         this.setDeletable();
50379     }
50380     this.isDirtyFlag = false;
50381         this.addEvents({
50382                 "drop" : true
50383         });
50384 };
50385
50386 Roo.extend(Roo.DDView, Roo.View, {
50387 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50388 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50389 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50390 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50391
50392         isFormField: true,
50393
50394         reset: Roo.emptyFn,
50395         
50396         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50397
50398         validate: function() {
50399                 return true;
50400         },
50401         
50402         destroy: function() {
50403                 this.purgeListeners();
50404                 this.getEl.removeAllListeners();
50405                 this.getEl().remove();
50406                 if (this.dragZone) {
50407                         if (this.dragZone.destroy) {
50408                                 this.dragZone.destroy();
50409                         }
50410                 }
50411                 if (this.dropZone) {
50412                         if (this.dropZone.destroy) {
50413                                 this.dropZone.destroy();
50414                         }
50415                 }
50416         },
50417
50418 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50419         getName: function() {
50420                 return this.name;
50421         },
50422
50423 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50424         setValue: function(v) {
50425                 if (!this.store) {
50426                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50427                 }
50428                 var data = {};
50429                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50430                 this.store.proxy = new Roo.data.MemoryProxy(data);
50431                 this.store.load();
50432         },
50433
50434 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50435         getValue: function() {
50436                 var result = '(';
50437                 this.store.each(function(rec) {
50438                         result += rec.id + ',';
50439                 });
50440                 return result.substr(0, result.length - 1) + ')';
50441         },
50442         
50443         getIds: function() {
50444                 var i = 0, result = new Array(this.store.getCount());
50445                 this.store.each(function(rec) {
50446                         result[i++] = rec.id;
50447                 });
50448                 return result;
50449         },
50450         
50451         isDirty: function() {
50452                 return this.isDirtyFlag;
50453         },
50454
50455 /**
50456  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50457  *      whole Element becomes the target, and this causes the drop gesture to append.
50458  */
50459     getTargetFromEvent : function(e) {
50460                 var target = e.getTarget();
50461                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50462                 target = target.parentNode;
50463                 }
50464                 if (!target) {
50465                         target = this.el.dom.lastChild || this.el.dom;
50466                 }
50467                 return target;
50468     },
50469
50470 /**
50471  *      Create the drag data which consists of an object which has the property "ddel" as
50472  *      the drag proxy element. 
50473  */
50474     getDragData : function(e) {
50475         var target = this.findItemFromChild(e.getTarget());
50476                 if(target) {
50477                         this.handleSelection(e);
50478                         var selNodes = this.getSelectedNodes();
50479             var dragData = {
50480                 source: this,
50481                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50482                 nodes: selNodes,
50483                 records: []
50484                         };
50485                         var selectedIndices = this.getSelectedIndexes();
50486                         for (var i = 0; i < selectedIndices.length; i++) {
50487                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50488                         }
50489                         if (selNodes.length == 1) {
50490                                 dragData.ddel = target.cloneNode(true); // the div element
50491                         } else {
50492                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50493                                 div.className = 'multi-proxy';
50494                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50495                                         div.appendChild(selNodes[i].cloneNode(true));
50496                                 }
50497                                 dragData.ddel = div;
50498                         }
50499             //console.log(dragData)
50500             //console.log(dragData.ddel.innerHTML)
50501                         return dragData;
50502                 }
50503         //console.log('nodragData')
50504                 return false;
50505     },
50506     
50507 /**     Specify to which ddGroup items in this DDView may be dragged. */
50508     setDraggable: function(ddGroup) {
50509         if (ddGroup instanceof Array) {
50510                 Roo.each(ddGroup, this.setDraggable, this);
50511                 return;
50512         }
50513         if (this.dragZone) {
50514                 this.dragZone.addToGroup(ddGroup);
50515         } else {
50516                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50517                                 containerScroll: true,
50518                                 ddGroup: ddGroup 
50519
50520                         });
50521 //                      Draggability implies selection. DragZone's mousedown selects the element.
50522                         if (!this.multiSelect) { this.singleSelect = true; }
50523
50524 //                      Wire the DragZone's handlers up to methods in *this*
50525                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50526                 }
50527     },
50528
50529 /**     Specify from which ddGroup this DDView accepts drops. */
50530     setDroppable: function(ddGroup) {
50531         if (ddGroup instanceof Array) {
50532                 Roo.each(ddGroup, this.setDroppable, this);
50533                 return;
50534         }
50535         if (this.dropZone) {
50536                 this.dropZone.addToGroup(ddGroup);
50537         } else {
50538                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50539                                 containerScroll: true,
50540                                 ddGroup: ddGroup
50541                         });
50542
50543 //                      Wire the DropZone's handlers up to methods in *this*
50544                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50545                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50546                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50547                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50548                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50549                 }
50550     },
50551
50552 /**     Decide whether to drop above or below a View node. */
50553     getDropPoint : function(e, n, dd){
50554         if (n == this.el.dom) { return "above"; }
50555                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50556                 var c = t + (b - t) / 2;
50557                 var y = Roo.lib.Event.getPageY(e);
50558                 if(y <= c) {
50559                         return "above";
50560                 }else{
50561                         return "below";
50562                 }
50563     },
50564
50565     onNodeEnter : function(n, dd, e, data){
50566                 return false;
50567     },
50568     
50569     onNodeOver : function(n, dd, e, data){
50570                 var pt = this.getDropPoint(e, n, dd);
50571                 // set the insert point style on the target node
50572                 var dragElClass = this.dropNotAllowed;
50573                 if (pt) {
50574                         var targetElClass;
50575                         if (pt == "above"){
50576                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50577                                 targetElClass = "x-view-drag-insert-above";
50578                         } else {
50579                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50580                                 targetElClass = "x-view-drag-insert-below";
50581                         }
50582                         if (this.lastInsertClass != targetElClass){
50583                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50584                                 this.lastInsertClass = targetElClass;
50585                         }
50586                 }
50587                 return dragElClass;
50588         },
50589
50590     onNodeOut : function(n, dd, e, data){
50591                 this.removeDropIndicators(n);
50592     },
50593
50594     onNodeDrop : function(n, dd, e, data){
50595         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50596                 return false;
50597         }
50598         var pt = this.getDropPoint(e, n, dd);
50599                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50600                 if (pt == "below") { insertAt++; }
50601                 for (var i = 0; i < data.records.length; i++) {
50602                         var r = data.records[i];
50603                         var dup = this.store.getById(r.id);
50604                         if (dup && (dd != this.dragZone)) {
50605                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50606                         } else {
50607                                 if (data.copy) {
50608                                         this.store.insert(insertAt++, r.copy());
50609                                 } else {
50610                                         data.source.isDirtyFlag = true;
50611                                         r.store.remove(r);
50612                                         this.store.insert(insertAt++, r);
50613                                 }
50614                                 this.isDirtyFlag = true;
50615                         }
50616                 }
50617                 this.dragZone.cachedTarget = null;
50618                 return true;
50619     },
50620
50621     removeDropIndicators : function(n){
50622                 if(n){
50623                         Roo.fly(n).removeClass([
50624                                 "x-view-drag-insert-above",
50625                                 "x-view-drag-insert-below"]);
50626                         this.lastInsertClass = "_noclass";
50627                 }
50628     },
50629
50630 /**
50631  *      Utility method. Add a delete option to the DDView's context menu.
50632  *      @param {String} imageUrl The URL of the "delete" icon image.
50633  */
50634         setDeletable: function(imageUrl) {
50635                 if (!this.singleSelect && !this.multiSelect) {
50636                         this.singleSelect = true;
50637                 }
50638                 var c = this.getContextMenu();
50639                 this.contextMenu.on("itemclick", function(item) {
50640                         switch (item.id) {
50641                                 case "delete":
50642                                         this.remove(this.getSelectedIndexes());
50643                                         break;
50644                         }
50645                 }, this);
50646                 this.contextMenu.add({
50647                         icon: imageUrl,
50648                         id: "delete",
50649                         text: 'Delete'
50650                 });
50651         },
50652         
50653 /**     Return the context menu for this DDView. */
50654         getContextMenu: function() {
50655                 if (!this.contextMenu) {
50656 //                      Create the View's context menu
50657                         this.contextMenu = new Roo.menu.Menu({
50658                                 id: this.id + "-contextmenu"
50659                         });
50660                         this.el.on("contextmenu", this.showContextMenu, this);
50661                 }
50662                 return this.contextMenu;
50663         },
50664         
50665         disableContextMenu: function() {
50666                 if (this.contextMenu) {
50667                         this.el.un("contextmenu", this.showContextMenu, this);
50668                 }
50669         },
50670
50671         showContextMenu: function(e, item) {
50672         item = this.findItemFromChild(e.getTarget());
50673                 if (item) {
50674                         e.stopEvent();
50675                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50676                         this.contextMenu.showAt(e.getXY());
50677             }
50678     },
50679
50680 /**
50681  *      Remove {@link Roo.data.Record}s at the specified indices.
50682  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50683  */
50684     remove: function(selectedIndices) {
50685                 selectedIndices = [].concat(selectedIndices);
50686                 for (var i = 0; i < selectedIndices.length; i++) {
50687                         var rec = this.store.getAt(selectedIndices[i]);
50688                         this.store.remove(rec);
50689                 }
50690     },
50691
50692 /**
50693  *      Double click fires the event, but also, if this is draggable, and there is only one other
50694  *      related DropZone, it transfers the selected node.
50695  */
50696     onDblClick : function(e){
50697         var item = this.findItemFromChild(e.getTarget());
50698         if(item){
50699             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50700                 return false;
50701             }
50702             if (this.dragGroup) {
50703                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50704                     while (targets.indexOf(this.dropZone) > -1) {
50705                             targets.remove(this.dropZone);
50706                                 }
50707                     if (targets.length == 1) {
50708                                         this.dragZone.cachedTarget = null;
50709                         var el = Roo.get(targets[0].getEl());
50710                         var box = el.getBox(true);
50711                         targets[0].onNodeDrop(el.dom, {
50712                                 target: el.dom,
50713                                 xy: [box.x, box.y + box.height - 1]
50714                         }, null, this.getDragData(e));
50715                     }
50716                 }
50717         }
50718     },
50719     
50720     handleSelection: function(e) {
50721                 this.dragZone.cachedTarget = null;
50722         var item = this.findItemFromChild(e.getTarget());
50723         if (!item) {
50724                 this.clearSelections(true);
50725                 return;
50726         }
50727                 if (item && (this.multiSelect || this.singleSelect)){
50728                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50729                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50730                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50731                                 this.unselect(item);
50732                         } else {
50733                                 this.select(item, this.multiSelect && e.ctrlKey);
50734                                 this.lastSelection = item;
50735                         }
50736                 }
50737     },
50738
50739     onItemClick : function(item, index, e){
50740                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50741                         return false;
50742                 }
50743                 return true;
50744     },
50745
50746     unselect : function(nodeInfo, suppressEvent){
50747                 var node = this.getNode(nodeInfo);
50748                 if(node && this.isSelected(node)){
50749                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50750                                 Roo.fly(node).removeClass(this.selectedClass);
50751                                 this.selections.remove(node);
50752                                 if(!suppressEvent){
50753                                         this.fireEvent("selectionchange", this, this.selections);
50754                                 }
50755                         }
50756                 }
50757     }
50758 });
50759 /*
50760  * Based on:
50761  * Ext JS Library 1.1.1
50762  * Copyright(c) 2006-2007, Ext JS, LLC.
50763  *
50764  * Originally Released Under LGPL - original licence link has changed is not relivant.
50765  *
50766  * Fork - LGPL
50767  * <script type="text/javascript">
50768  */
50769  
50770 /**
50771  * @class Roo.LayoutManager
50772  * @extends Roo.util.Observable
50773  * Base class for layout managers.
50774  */
50775 Roo.LayoutManager = function(container, config){
50776     Roo.LayoutManager.superclass.constructor.call(this);
50777     this.el = Roo.get(container);
50778     // ie scrollbar fix
50779     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50780         document.body.scroll = "no";
50781     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50782         this.el.position('relative');
50783     }
50784     this.id = this.el.id;
50785     this.el.addClass("x-layout-container");
50786     /** false to disable window resize monitoring @type Boolean */
50787     this.monitorWindowResize = true;
50788     this.regions = {};
50789     this.addEvents({
50790         /**
50791          * @event layout
50792          * Fires when a layout is performed. 
50793          * @param {Roo.LayoutManager} this
50794          */
50795         "layout" : true,
50796         /**
50797          * @event regionresized
50798          * Fires when the user resizes a region. 
50799          * @param {Roo.LayoutRegion} region The resized region
50800          * @param {Number} newSize The new size (width for east/west, height for north/south)
50801          */
50802         "regionresized" : true,
50803         /**
50804          * @event regioncollapsed
50805          * Fires when a region is collapsed. 
50806          * @param {Roo.LayoutRegion} region The collapsed region
50807          */
50808         "regioncollapsed" : true,
50809         /**
50810          * @event regionexpanded
50811          * Fires when a region is expanded.  
50812          * @param {Roo.LayoutRegion} region The expanded region
50813          */
50814         "regionexpanded" : true
50815     });
50816     this.updating = false;
50817     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50818 };
50819
50820 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50821     /**
50822      * Returns true if this layout is currently being updated
50823      * @return {Boolean}
50824      */
50825     isUpdating : function(){
50826         return this.updating; 
50827     },
50828     
50829     /**
50830      * Suspend the LayoutManager from doing auto-layouts while
50831      * making multiple add or remove calls
50832      */
50833     beginUpdate : function(){
50834         this.updating = true;    
50835     },
50836     
50837     /**
50838      * Restore auto-layouts and optionally disable the manager from performing a layout
50839      * @param {Boolean} noLayout true to disable a layout update 
50840      */
50841     endUpdate : function(noLayout){
50842         this.updating = false;
50843         if(!noLayout){
50844             this.layout();
50845         }    
50846     },
50847     
50848     layout: function(){
50849         
50850     },
50851     
50852     onRegionResized : function(region, newSize){
50853         this.fireEvent("regionresized", region, newSize);
50854         this.layout();
50855     },
50856     
50857     onRegionCollapsed : function(region){
50858         this.fireEvent("regioncollapsed", region);
50859     },
50860     
50861     onRegionExpanded : function(region){
50862         this.fireEvent("regionexpanded", region);
50863     },
50864         
50865     /**
50866      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
50867      * performs box-model adjustments.
50868      * @return {Object} The size as an object {width: (the width), height: (the height)}
50869      */
50870     getViewSize : function(){
50871         var size;
50872         if(this.el.dom != document.body){
50873             size = this.el.getSize();
50874         }else{
50875             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
50876         }
50877         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
50878         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
50879         return size;
50880     },
50881     
50882     /**
50883      * Returns the Element this layout is bound to.
50884      * @return {Roo.Element}
50885      */
50886     getEl : function(){
50887         return this.el;
50888     },
50889     
50890     /**
50891      * Returns the specified region.
50892      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
50893      * @return {Roo.LayoutRegion}
50894      */
50895     getRegion : function(target){
50896         return this.regions[target.toLowerCase()];
50897     },
50898     
50899     onWindowResize : function(){
50900         if(this.monitorWindowResize){
50901             this.layout();
50902         }
50903     }
50904 });/*
50905  * Based on:
50906  * Ext JS Library 1.1.1
50907  * Copyright(c) 2006-2007, Ext JS, LLC.
50908  *
50909  * Originally Released Under LGPL - original licence link has changed is not relivant.
50910  *
50911  * Fork - LGPL
50912  * <script type="text/javascript">
50913  */
50914 /**
50915  * @class Roo.BorderLayout
50916  * @extends Roo.LayoutManager
50917  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
50918  * please see: <br><br>
50919  * <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>
50920  * <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>
50921  * Example:
50922  <pre><code>
50923  var layout = new Roo.BorderLayout(document.body, {
50924     north: {
50925         initialSize: 25,
50926         titlebar: false
50927     },
50928     west: {
50929         split:true,
50930         initialSize: 200,
50931         minSize: 175,
50932         maxSize: 400,
50933         titlebar: true,
50934         collapsible: true
50935     },
50936     east: {
50937         split:true,
50938         initialSize: 202,
50939         minSize: 175,
50940         maxSize: 400,
50941         titlebar: true,
50942         collapsible: true
50943     },
50944     south: {
50945         split:true,
50946         initialSize: 100,
50947         minSize: 100,
50948         maxSize: 200,
50949         titlebar: true,
50950         collapsible: true
50951     },
50952     center: {
50953         titlebar: true,
50954         autoScroll:true,
50955         resizeTabs: true,
50956         minTabWidth: 50,
50957         preferredTabWidth: 150
50958     }
50959 });
50960
50961 // shorthand
50962 var CP = Roo.ContentPanel;
50963
50964 layout.beginUpdate();
50965 layout.add("north", new CP("north", "North"));
50966 layout.add("south", new CP("south", {title: "South", closable: true}));
50967 layout.add("west", new CP("west", {title: "West"}));
50968 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
50969 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
50970 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
50971 layout.getRegion("center").showPanel("center1");
50972 layout.endUpdate();
50973 </code></pre>
50974
50975 <b>The container the layout is rendered into can be either the body element or any other element.
50976 If it is not the body element, the container needs to either be an absolute positioned element,
50977 or you will need to add "position:relative" to the css of the container.  You will also need to specify
50978 the container size if it is not the body element.</b>
50979
50980 * @constructor
50981 * Create a new BorderLayout
50982 * @param {String/HTMLElement/Element} container The container this layout is bound to
50983 * @param {Object} config Configuration options
50984  */
50985 Roo.BorderLayout = function(container, config){
50986     config = config || {};
50987     Roo.BorderLayout.superclass.constructor.call(this, container, config);
50988     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
50989     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
50990         var target = this.factory.validRegions[i];
50991         if(config[target]){
50992             this.addRegion(target, config[target]);
50993         }
50994     }
50995 };
50996
50997 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
50998     /**
50999      * Creates and adds a new region if it doesn't already exist.
51000      * @param {String} target The target region key (north, south, east, west or center).
51001      * @param {Object} config The regions config object
51002      * @return {BorderLayoutRegion} The new region
51003      */
51004     addRegion : function(target, config){
51005         if(!this.regions[target]){
51006             var r = this.factory.create(target, this, config);
51007             this.bindRegion(target, r);
51008         }
51009         return this.regions[target];
51010     },
51011
51012     // private (kinda)
51013     bindRegion : function(name, r){
51014         this.regions[name] = r;
51015         r.on("visibilitychange", this.layout, this);
51016         r.on("paneladded", this.layout, this);
51017         r.on("panelremoved", this.layout, this);
51018         r.on("invalidated", this.layout, this);
51019         r.on("resized", this.onRegionResized, this);
51020         r.on("collapsed", this.onRegionCollapsed, this);
51021         r.on("expanded", this.onRegionExpanded, this);
51022     },
51023
51024     /**
51025      * Performs a layout update.
51026      */
51027     layout : function(){
51028         if(this.updating) {
51029             return;
51030         }
51031         var size = this.getViewSize();
51032         var w = size.width;
51033         var h = size.height;
51034         var centerW = w;
51035         var centerH = h;
51036         var centerY = 0;
51037         var centerX = 0;
51038         //var x = 0, y = 0;
51039
51040         var rs = this.regions;
51041         var north = rs["north"];
51042         var south = rs["south"]; 
51043         var west = rs["west"];
51044         var east = rs["east"];
51045         var center = rs["center"];
51046         //if(this.hideOnLayout){ // not supported anymore
51047             //c.el.setStyle("display", "none");
51048         //}
51049         if(north && north.isVisible()){
51050             var b = north.getBox();
51051             var m = north.getMargins();
51052             b.width = w - (m.left+m.right);
51053             b.x = m.left;
51054             b.y = m.top;
51055             centerY = b.height + b.y + m.bottom;
51056             centerH -= centerY;
51057             north.updateBox(this.safeBox(b));
51058         }
51059         if(south && south.isVisible()){
51060             var b = south.getBox();
51061             var m = south.getMargins();
51062             b.width = w - (m.left+m.right);
51063             b.x = m.left;
51064             var totalHeight = (b.height + m.top + m.bottom);
51065             b.y = h - totalHeight + m.top;
51066             centerH -= totalHeight;
51067             south.updateBox(this.safeBox(b));
51068         }
51069         if(west && west.isVisible()){
51070             var b = west.getBox();
51071             var m = west.getMargins();
51072             b.height = centerH - (m.top+m.bottom);
51073             b.x = m.left;
51074             b.y = centerY + m.top;
51075             var totalWidth = (b.width + m.left + m.right);
51076             centerX += totalWidth;
51077             centerW -= totalWidth;
51078             west.updateBox(this.safeBox(b));
51079         }
51080         if(east && east.isVisible()){
51081             var b = east.getBox();
51082             var m = east.getMargins();
51083             b.height = centerH - (m.top+m.bottom);
51084             var totalWidth = (b.width + m.left + m.right);
51085             b.x = w - totalWidth + m.left;
51086             b.y = centerY + m.top;
51087             centerW -= totalWidth;
51088             east.updateBox(this.safeBox(b));
51089         }
51090         if(center){
51091             var m = center.getMargins();
51092             var centerBox = {
51093                 x: centerX + m.left,
51094                 y: centerY + m.top,
51095                 width: centerW - (m.left+m.right),
51096                 height: centerH - (m.top+m.bottom)
51097             };
51098             //if(this.hideOnLayout){
51099                 //center.el.setStyle("display", "block");
51100             //}
51101             center.updateBox(this.safeBox(centerBox));
51102         }
51103         this.el.repaint();
51104         this.fireEvent("layout", this);
51105     },
51106
51107     // private
51108     safeBox : function(box){
51109         box.width = Math.max(0, box.width);
51110         box.height = Math.max(0, box.height);
51111         return box;
51112     },
51113
51114     /**
51115      * Adds a ContentPanel (or subclass) to this layout.
51116      * @param {String} target The target region key (north, south, east, west or center).
51117      * @param {Roo.ContentPanel} panel The panel to add
51118      * @return {Roo.ContentPanel} The added panel
51119      */
51120     add : function(target, panel){
51121          
51122         target = target.toLowerCase();
51123         return this.regions[target].add(panel);
51124     },
51125
51126     /**
51127      * Remove a ContentPanel (or subclass) to this layout.
51128      * @param {String} target The target region key (north, south, east, west or center).
51129      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51130      * @return {Roo.ContentPanel} The removed panel
51131      */
51132     remove : function(target, panel){
51133         target = target.toLowerCase();
51134         return this.regions[target].remove(panel);
51135     },
51136
51137     /**
51138      * Searches all regions for a panel with the specified id
51139      * @param {String} panelId
51140      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51141      */
51142     findPanel : function(panelId){
51143         var rs = this.regions;
51144         for(var target in rs){
51145             if(typeof rs[target] != "function"){
51146                 var p = rs[target].getPanel(panelId);
51147                 if(p){
51148                     return p;
51149                 }
51150             }
51151         }
51152         return null;
51153     },
51154
51155     /**
51156      * Searches all regions for a panel with the specified id and activates (shows) it.
51157      * @param {String/ContentPanel} panelId The panels id or the panel itself
51158      * @return {Roo.ContentPanel} The shown panel or null
51159      */
51160     showPanel : function(panelId) {
51161       var rs = this.regions;
51162       for(var target in rs){
51163          var r = rs[target];
51164          if(typeof r != "function"){
51165             if(r.hasPanel(panelId)){
51166                return r.showPanel(panelId);
51167             }
51168          }
51169       }
51170       return null;
51171    },
51172
51173    /**
51174      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51175      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51176      */
51177     restoreState : function(provider){
51178         if(!provider){
51179             provider = Roo.state.Manager;
51180         }
51181         var sm = new Roo.LayoutStateManager();
51182         sm.init(this, provider);
51183     },
51184
51185     /**
51186      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51187      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51188      * a valid ContentPanel config object.  Example:
51189      * <pre><code>
51190 // Create the main layout
51191 var layout = new Roo.BorderLayout('main-ct', {
51192     west: {
51193         split:true,
51194         minSize: 175,
51195         titlebar: true
51196     },
51197     center: {
51198         title:'Components'
51199     }
51200 }, 'main-ct');
51201
51202 // Create and add multiple ContentPanels at once via configs
51203 layout.batchAdd({
51204    west: {
51205        id: 'source-files',
51206        autoCreate:true,
51207        title:'Ext Source Files',
51208        autoScroll:true,
51209        fitToFrame:true
51210    },
51211    center : {
51212        el: cview,
51213        autoScroll:true,
51214        fitToFrame:true,
51215        toolbar: tb,
51216        resizeEl:'cbody'
51217    }
51218 });
51219 </code></pre>
51220      * @param {Object} regions An object containing ContentPanel configs by region name
51221      */
51222     batchAdd : function(regions){
51223         this.beginUpdate();
51224         for(var rname in regions){
51225             var lr = this.regions[rname];
51226             if(lr){
51227                 this.addTypedPanels(lr, regions[rname]);
51228             }
51229         }
51230         this.endUpdate();
51231     },
51232
51233     // private
51234     addTypedPanels : function(lr, ps){
51235         if(typeof ps == 'string'){
51236             lr.add(new Roo.ContentPanel(ps));
51237         }
51238         else if(ps instanceof Array){
51239             for(var i =0, len = ps.length; i < len; i++){
51240                 this.addTypedPanels(lr, ps[i]);
51241             }
51242         }
51243         else if(!ps.events){ // raw config?
51244             var el = ps.el;
51245             delete ps.el; // prevent conflict
51246             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51247         }
51248         else {  // panel object assumed!
51249             lr.add(ps);
51250         }
51251     },
51252     /**
51253      * Adds a xtype elements to the layout.
51254      * <pre><code>
51255
51256 layout.addxtype({
51257        xtype : 'ContentPanel',
51258        region: 'west',
51259        items: [ .... ]
51260    }
51261 );
51262
51263 layout.addxtype({
51264         xtype : 'NestedLayoutPanel',
51265         region: 'west',
51266         layout: {
51267            center: { },
51268            west: { }   
51269         },
51270         items : [ ... list of content panels or nested layout panels.. ]
51271    }
51272 );
51273 </code></pre>
51274      * @param {Object} cfg Xtype definition of item to add.
51275      */
51276     addxtype : function(cfg)
51277     {
51278         // basically accepts a pannel...
51279         // can accept a layout region..!?!?
51280         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51281         
51282         if (!cfg.xtype.match(/Panel$/)) {
51283             return false;
51284         }
51285         var ret = false;
51286         
51287         if (typeof(cfg.region) == 'undefined') {
51288             Roo.log("Failed to add Panel, region was not set");
51289             Roo.log(cfg);
51290             return false;
51291         }
51292         var region = cfg.region;
51293         delete cfg.region;
51294         
51295           
51296         var xitems = [];
51297         if (cfg.items) {
51298             xitems = cfg.items;
51299             delete cfg.items;
51300         }
51301         var nb = false;
51302         
51303         switch(cfg.xtype) 
51304         {
51305             case 'ContentPanel':  // ContentPanel (el, cfg)
51306             case 'ScrollPanel':  // ContentPanel (el, cfg)
51307             case 'ViewPanel': 
51308                 if(cfg.autoCreate) {
51309                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51310                 } else {
51311                     var el = this.el.createChild();
51312                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51313                 }
51314                 
51315                 this.add(region, ret);
51316                 break;
51317             
51318             
51319             case 'TreePanel': // our new panel!
51320                 cfg.el = this.el.createChild();
51321                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51322                 this.add(region, ret);
51323                 break;
51324             
51325             case 'NestedLayoutPanel': 
51326                 // create a new Layout (which is  a Border Layout...
51327                 var el = this.el.createChild();
51328                 var clayout = cfg.layout;
51329                 delete cfg.layout;
51330                 clayout.items   = clayout.items  || [];
51331                 // replace this exitems with the clayout ones..
51332                 xitems = clayout.items;
51333                  
51334                 
51335                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51336                     cfg.background = false;
51337                 }
51338                 var layout = new Roo.BorderLayout(el, clayout);
51339                 
51340                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51341                 //console.log('adding nested layout panel '  + cfg.toSource());
51342                 this.add(region, ret);
51343                 nb = {}; /// find first...
51344                 break;
51345                 
51346             case 'GridPanel': 
51347             
51348                 // needs grid and region
51349                 
51350                 //var el = this.getRegion(region).el.createChild();
51351                 var el = this.el.createChild();
51352                 // create the grid first...
51353                 
51354                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51355                 delete cfg.grid;
51356                 if (region == 'center' && this.active ) {
51357                     cfg.background = false;
51358                 }
51359                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51360                 
51361                 this.add(region, ret);
51362                 if (cfg.background) {
51363                     ret.on('activate', function(gp) {
51364                         if (!gp.grid.rendered) {
51365                             gp.grid.render();
51366                         }
51367                     });
51368                 } else {
51369                     grid.render();
51370                 }
51371                 break;
51372            
51373            
51374            
51375                 
51376                 
51377                 
51378             default:
51379                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51380                     
51381                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51382                     this.add(region, ret);
51383                 } else {
51384                 
51385                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51386                     return null;
51387                 }
51388                 
51389              // GridPanel (grid, cfg)
51390             
51391         }
51392         this.beginUpdate();
51393         // add children..
51394         var region = '';
51395         var abn = {};
51396         Roo.each(xitems, function(i)  {
51397             region = nb && i.region ? i.region : false;
51398             
51399             var add = ret.addxtype(i);
51400            
51401             if (region) {
51402                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51403                 if (!i.background) {
51404                     abn[region] = nb[region] ;
51405                 }
51406             }
51407             
51408         });
51409         this.endUpdate();
51410
51411         // make the last non-background panel active..
51412         //if (nb) { Roo.log(abn); }
51413         if (nb) {
51414             
51415             for(var r in abn) {
51416                 region = this.getRegion(r);
51417                 if (region) {
51418                     // tried using nb[r], but it does not work..
51419                      
51420                     region.showPanel(abn[r]);
51421                    
51422                 }
51423             }
51424         }
51425         return ret;
51426         
51427     }
51428 });
51429
51430 /**
51431  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51432  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51433  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51434  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51435  * <pre><code>
51436 // shorthand
51437 var CP = Roo.ContentPanel;
51438
51439 var layout = Roo.BorderLayout.create({
51440     north: {
51441         initialSize: 25,
51442         titlebar: false,
51443         panels: [new CP("north", "North")]
51444     },
51445     west: {
51446         split:true,
51447         initialSize: 200,
51448         minSize: 175,
51449         maxSize: 400,
51450         titlebar: true,
51451         collapsible: true,
51452         panels: [new CP("west", {title: "West"})]
51453     },
51454     east: {
51455         split:true,
51456         initialSize: 202,
51457         minSize: 175,
51458         maxSize: 400,
51459         titlebar: true,
51460         collapsible: true,
51461         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51462     },
51463     south: {
51464         split:true,
51465         initialSize: 100,
51466         minSize: 100,
51467         maxSize: 200,
51468         titlebar: true,
51469         collapsible: true,
51470         panels: [new CP("south", {title: "South", closable: true})]
51471     },
51472     center: {
51473         titlebar: true,
51474         autoScroll:true,
51475         resizeTabs: true,
51476         minTabWidth: 50,
51477         preferredTabWidth: 150,
51478         panels: [
51479             new CP("center1", {title: "Close Me", closable: true}),
51480             new CP("center2", {title: "Center Panel", closable: false})
51481         ]
51482     }
51483 }, document.body);
51484
51485 layout.getRegion("center").showPanel("center1");
51486 </code></pre>
51487  * @param config
51488  * @param targetEl
51489  */
51490 Roo.BorderLayout.create = function(config, targetEl){
51491     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51492     layout.beginUpdate();
51493     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51494     for(var j = 0, jlen = regions.length; j < jlen; j++){
51495         var lr = regions[j];
51496         if(layout.regions[lr] && config[lr].panels){
51497             var r = layout.regions[lr];
51498             var ps = config[lr].panels;
51499             layout.addTypedPanels(r, ps);
51500         }
51501     }
51502     layout.endUpdate();
51503     return layout;
51504 };
51505
51506 // private
51507 Roo.BorderLayout.RegionFactory = {
51508     // private
51509     validRegions : ["north","south","east","west","center"],
51510
51511     // private
51512     create : function(target, mgr, config){
51513         target = target.toLowerCase();
51514         if(config.lightweight || config.basic){
51515             return new Roo.BasicLayoutRegion(mgr, config, target);
51516         }
51517         switch(target){
51518             case "north":
51519                 return new Roo.NorthLayoutRegion(mgr, config);
51520             case "south":
51521                 return new Roo.SouthLayoutRegion(mgr, config);
51522             case "east":
51523                 return new Roo.EastLayoutRegion(mgr, config);
51524             case "west":
51525                 return new Roo.WestLayoutRegion(mgr, config);
51526             case "center":
51527                 return new Roo.CenterLayoutRegion(mgr, config);
51528         }
51529         throw 'Layout region "'+target+'" not supported.';
51530     }
51531 };/*
51532  * Based on:
51533  * Ext JS Library 1.1.1
51534  * Copyright(c) 2006-2007, Ext JS, LLC.
51535  *
51536  * Originally Released Under LGPL - original licence link has changed is not relivant.
51537  *
51538  * Fork - LGPL
51539  * <script type="text/javascript">
51540  */
51541  
51542 /**
51543  * @class Roo.BasicLayoutRegion
51544  * @extends Roo.util.Observable
51545  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51546  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51547  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51548  */
51549 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51550     this.mgr = mgr;
51551     this.position  = pos;
51552     this.events = {
51553         /**
51554          * @scope Roo.BasicLayoutRegion
51555          */
51556         
51557         /**
51558          * @event beforeremove
51559          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51560          * @param {Roo.LayoutRegion} this
51561          * @param {Roo.ContentPanel} panel The panel
51562          * @param {Object} e The cancel event object
51563          */
51564         "beforeremove" : true,
51565         /**
51566          * @event invalidated
51567          * Fires when the layout for this region is changed.
51568          * @param {Roo.LayoutRegion} this
51569          */
51570         "invalidated" : true,
51571         /**
51572          * @event visibilitychange
51573          * Fires when this region is shown or hidden 
51574          * @param {Roo.LayoutRegion} this
51575          * @param {Boolean} visibility true or false
51576          */
51577         "visibilitychange" : true,
51578         /**
51579          * @event paneladded
51580          * Fires when a panel is added. 
51581          * @param {Roo.LayoutRegion} this
51582          * @param {Roo.ContentPanel} panel The panel
51583          */
51584         "paneladded" : true,
51585         /**
51586          * @event panelremoved
51587          * Fires when a panel is removed. 
51588          * @param {Roo.LayoutRegion} this
51589          * @param {Roo.ContentPanel} panel The panel
51590          */
51591         "panelremoved" : true,
51592         /**
51593          * @event beforecollapse
51594          * Fires when this region before collapse.
51595          * @param {Roo.LayoutRegion} this
51596          */
51597         "beforecollapse" : true,
51598         /**
51599          * @event collapsed
51600          * Fires when this region is collapsed.
51601          * @param {Roo.LayoutRegion} this
51602          */
51603         "collapsed" : true,
51604         /**
51605          * @event expanded
51606          * Fires when this region is expanded.
51607          * @param {Roo.LayoutRegion} this
51608          */
51609         "expanded" : true,
51610         /**
51611          * @event slideshow
51612          * Fires when this region is slid into view.
51613          * @param {Roo.LayoutRegion} this
51614          */
51615         "slideshow" : true,
51616         /**
51617          * @event slidehide
51618          * Fires when this region slides out of view. 
51619          * @param {Roo.LayoutRegion} this
51620          */
51621         "slidehide" : true,
51622         /**
51623          * @event panelactivated
51624          * Fires when a panel is activated. 
51625          * @param {Roo.LayoutRegion} this
51626          * @param {Roo.ContentPanel} panel The activated panel
51627          */
51628         "panelactivated" : true,
51629         /**
51630          * @event resized
51631          * Fires when the user resizes this region. 
51632          * @param {Roo.LayoutRegion} this
51633          * @param {Number} newSize The new size (width for east/west, height for north/south)
51634          */
51635         "resized" : true
51636     };
51637     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51638     this.panels = new Roo.util.MixedCollection();
51639     this.panels.getKey = this.getPanelId.createDelegate(this);
51640     this.box = null;
51641     this.activePanel = null;
51642     // ensure listeners are added...
51643     
51644     if (config.listeners || config.events) {
51645         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51646             listeners : config.listeners || {},
51647             events : config.events || {}
51648         });
51649     }
51650     
51651     if(skipConfig !== true){
51652         this.applyConfig(config);
51653     }
51654 };
51655
51656 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51657     getPanelId : function(p){
51658         return p.getId();
51659     },
51660     
51661     applyConfig : function(config){
51662         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51663         this.config = config;
51664         
51665     },
51666     
51667     /**
51668      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51669      * the width, for horizontal (north, south) the height.
51670      * @param {Number} newSize The new width or height
51671      */
51672     resizeTo : function(newSize){
51673         var el = this.el ? this.el :
51674                  (this.activePanel ? this.activePanel.getEl() : null);
51675         if(el){
51676             switch(this.position){
51677                 case "east":
51678                 case "west":
51679                     el.setWidth(newSize);
51680                     this.fireEvent("resized", this, newSize);
51681                 break;
51682                 case "north":
51683                 case "south":
51684                     el.setHeight(newSize);
51685                     this.fireEvent("resized", this, newSize);
51686                 break;                
51687             }
51688         }
51689     },
51690     
51691     getBox : function(){
51692         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51693     },
51694     
51695     getMargins : function(){
51696         return this.margins;
51697     },
51698     
51699     updateBox : function(box){
51700         this.box = box;
51701         var el = this.activePanel.getEl();
51702         el.dom.style.left = box.x + "px";
51703         el.dom.style.top = box.y + "px";
51704         this.activePanel.setSize(box.width, box.height);
51705     },
51706     
51707     /**
51708      * Returns the container element for this region.
51709      * @return {Roo.Element}
51710      */
51711     getEl : function(){
51712         return this.activePanel;
51713     },
51714     
51715     /**
51716      * Returns true if this region is currently visible.
51717      * @return {Boolean}
51718      */
51719     isVisible : function(){
51720         return this.activePanel ? true : false;
51721     },
51722     
51723     setActivePanel : function(panel){
51724         panel = this.getPanel(panel);
51725         if(this.activePanel && this.activePanel != panel){
51726             this.activePanel.setActiveState(false);
51727             this.activePanel.getEl().setLeftTop(-10000,-10000);
51728         }
51729         this.activePanel = panel;
51730         panel.setActiveState(true);
51731         if(this.box){
51732             panel.setSize(this.box.width, this.box.height);
51733         }
51734         this.fireEvent("panelactivated", this, panel);
51735         this.fireEvent("invalidated");
51736     },
51737     
51738     /**
51739      * Show the specified panel.
51740      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51741      * @return {Roo.ContentPanel} The shown panel or null
51742      */
51743     showPanel : function(panel){
51744         if(panel = this.getPanel(panel)){
51745             this.setActivePanel(panel);
51746         }
51747         return panel;
51748     },
51749     
51750     /**
51751      * Get the active panel for this region.
51752      * @return {Roo.ContentPanel} The active panel or null
51753      */
51754     getActivePanel : function(){
51755         return this.activePanel;
51756     },
51757     
51758     /**
51759      * Add the passed ContentPanel(s)
51760      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51761      * @return {Roo.ContentPanel} The panel added (if only one was added)
51762      */
51763     add : function(panel){
51764         if(arguments.length > 1){
51765             for(var i = 0, len = arguments.length; i < len; i++) {
51766                 this.add(arguments[i]);
51767             }
51768             return null;
51769         }
51770         if(this.hasPanel(panel)){
51771             this.showPanel(panel);
51772             return panel;
51773         }
51774         var el = panel.getEl();
51775         if(el.dom.parentNode != this.mgr.el.dom){
51776             this.mgr.el.dom.appendChild(el.dom);
51777         }
51778         if(panel.setRegion){
51779             panel.setRegion(this);
51780         }
51781         this.panels.add(panel);
51782         el.setStyle("position", "absolute");
51783         if(!panel.background){
51784             this.setActivePanel(panel);
51785             if(this.config.initialSize && this.panels.getCount()==1){
51786                 this.resizeTo(this.config.initialSize);
51787             }
51788         }
51789         this.fireEvent("paneladded", this, panel);
51790         return panel;
51791     },
51792     
51793     /**
51794      * Returns true if the panel is in this region.
51795      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51796      * @return {Boolean}
51797      */
51798     hasPanel : function(panel){
51799         if(typeof panel == "object"){ // must be panel obj
51800             panel = panel.getId();
51801         }
51802         return this.getPanel(panel) ? true : false;
51803     },
51804     
51805     /**
51806      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51807      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51808      * @param {Boolean} preservePanel Overrides the config preservePanel option
51809      * @return {Roo.ContentPanel} The panel that was removed
51810      */
51811     remove : function(panel, preservePanel){
51812         panel = this.getPanel(panel);
51813         if(!panel){
51814             return null;
51815         }
51816         var e = {};
51817         this.fireEvent("beforeremove", this, panel, e);
51818         if(e.cancel === true){
51819             return null;
51820         }
51821         var panelId = panel.getId();
51822         this.panels.removeKey(panelId);
51823         return panel;
51824     },
51825     
51826     /**
51827      * Returns the panel specified or null if it's not in this region.
51828      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51829      * @return {Roo.ContentPanel}
51830      */
51831     getPanel : function(id){
51832         if(typeof id == "object"){ // must be panel obj
51833             return id;
51834         }
51835         return this.panels.get(id);
51836     },
51837     
51838     /**
51839      * Returns this regions position (north/south/east/west/center).
51840      * @return {String} 
51841      */
51842     getPosition: function(){
51843         return this.position;    
51844     }
51845 });/*
51846  * Based on:
51847  * Ext JS Library 1.1.1
51848  * Copyright(c) 2006-2007, Ext JS, LLC.
51849  *
51850  * Originally Released Under LGPL - original licence link has changed is not relivant.
51851  *
51852  * Fork - LGPL
51853  * <script type="text/javascript">
51854  */
51855  
51856 /**
51857  * @class Roo.LayoutRegion
51858  * @extends Roo.BasicLayoutRegion
51859  * This class represents a region in a layout manager.
51860  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
51861  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
51862  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
51863  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
51864  * @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})
51865  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
51866  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
51867  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
51868  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
51869  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
51870  * @cfg {String}    title           The title for the region (overrides panel titles)
51871  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
51872  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
51873  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
51874  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
51875  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
51876  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
51877  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
51878  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
51879  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
51880  * @cfg {Boolean}   showPin         True to show a pin button
51881  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
51882  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
51883  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
51884  * @cfg {Number}    width           For East/West panels
51885  * @cfg {Number}    height          For North/South panels
51886  * @cfg {Boolean}   split           To show the splitter
51887  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
51888  */
51889 Roo.LayoutRegion = function(mgr, config, pos){
51890     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
51891     var dh = Roo.DomHelper;
51892     /** This region's container element 
51893     * @type Roo.Element */
51894     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
51895     /** This region's title element 
51896     * @type Roo.Element */
51897
51898     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
51899         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
51900         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
51901     ]}, true);
51902     this.titleEl.enableDisplayMode();
51903     /** This region's title text element 
51904     * @type HTMLElement */
51905     this.titleTextEl = this.titleEl.dom.firstChild;
51906     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
51907     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
51908     this.closeBtn.enableDisplayMode();
51909     this.closeBtn.on("click", this.closeClicked, this);
51910     this.closeBtn.hide();
51911
51912     this.createBody(config);
51913     this.visible = true;
51914     this.collapsed = false;
51915
51916     if(config.hideWhenEmpty){
51917         this.hide();
51918         this.on("paneladded", this.validateVisibility, this);
51919         this.on("panelremoved", this.validateVisibility, this);
51920     }
51921     this.applyConfig(config);
51922 };
51923
51924 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
51925
51926     createBody : function(){
51927         /** This region's body element 
51928         * @type Roo.Element */
51929         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
51930     },
51931
51932     applyConfig : function(c){
51933         if(c.collapsible && this.position != "center" && !this.collapsedEl){
51934             var dh = Roo.DomHelper;
51935             if(c.titlebar !== false){
51936                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
51937                 this.collapseBtn.on("click", this.collapse, this);
51938                 this.collapseBtn.enableDisplayMode();
51939
51940                 if(c.showPin === true || this.showPin){
51941                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
51942                     this.stickBtn.enableDisplayMode();
51943                     this.stickBtn.on("click", this.expand, this);
51944                     this.stickBtn.hide();
51945                 }
51946             }
51947             /** This region's collapsed element
51948             * @type Roo.Element */
51949             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
51950                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
51951             ]}, true);
51952             if(c.floatable !== false){
51953                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
51954                this.collapsedEl.on("click", this.collapseClick, this);
51955             }
51956
51957             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
51958                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
51959                    id: "message", unselectable: "on", style:{"float":"left"}});
51960                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
51961              }
51962             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
51963             this.expandBtn.on("click", this.expand, this);
51964         }
51965         if(this.collapseBtn){
51966             this.collapseBtn.setVisible(c.collapsible == true);
51967         }
51968         this.cmargins = c.cmargins || this.cmargins ||
51969                          (this.position == "west" || this.position == "east" ?
51970                              {top: 0, left: 2, right:2, bottom: 0} :
51971                              {top: 2, left: 0, right:0, bottom: 2});
51972         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51973         this.bottomTabs = c.tabPosition != "top";
51974         this.autoScroll = c.autoScroll || false;
51975         if(this.autoScroll){
51976             this.bodyEl.setStyle("overflow", "auto");
51977         }else{
51978             this.bodyEl.setStyle("overflow", "hidden");
51979         }
51980         //if(c.titlebar !== false){
51981             if((!c.titlebar && !c.title) || c.titlebar === false){
51982                 this.titleEl.hide();
51983             }else{
51984                 this.titleEl.show();
51985                 if(c.title){
51986                     this.titleTextEl.innerHTML = c.title;
51987                 }
51988             }
51989         //}
51990         this.duration = c.duration || .30;
51991         this.slideDuration = c.slideDuration || .45;
51992         this.config = c;
51993         if(c.collapsed){
51994             this.collapse(true);
51995         }
51996         if(c.hidden){
51997             this.hide();
51998         }
51999     },
52000     /**
52001      * Returns true if this region is currently visible.
52002      * @return {Boolean}
52003      */
52004     isVisible : function(){
52005         return this.visible;
52006     },
52007
52008     /**
52009      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
52010      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
52011      */
52012     setCollapsedTitle : function(title){
52013         title = title || "&#160;";
52014         if(this.collapsedTitleTextEl){
52015             this.collapsedTitleTextEl.innerHTML = title;
52016         }
52017     },
52018
52019     getBox : function(){
52020         var b;
52021         if(!this.collapsed){
52022             b = this.el.getBox(false, true);
52023         }else{
52024             b = this.collapsedEl.getBox(false, true);
52025         }
52026         return b;
52027     },
52028
52029     getMargins : function(){
52030         return this.collapsed ? this.cmargins : this.margins;
52031     },
52032
52033     highlight : function(){
52034         this.el.addClass("x-layout-panel-dragover");
52035     },
52036
52037     unhighlight : function(){
52038         this.el.removeClass("x-layout-panel-dragover");
52039     },
52040
52041     updateBox : function(box){
52042         this.box = box;
52043         if(!this.collapsed){
52044             this.el.dom.style.left = box.x + "px";
52045             this.el.dom.style.top = box.y + "px";
52046             this.updateBody(box.width, box.height);
52047         }else{
52048             this.collapsedEl.dom.style.left = box.x + "px";
52049             this.collapsedEl.dom.style.top = box.y + "px";
52050             this.collapsedEl.setSize(box.width, box.height);
52051         }
52052         if(this.tabs){
52053             this.tabs.autoSizeTabs();
52054         }
52055     },
52056
52057     updateBody : function(w, h){
52058         if(w !== null){
52059             this.el.setWidth(w);
52060             w -= this.el.getBorderWidth("rl");
52061             if(this.config.adjustments){
52062                 w += this.config.adjustments[0];
52063             }
52064         }
52065         if(h !== null){
52066             this.el.setHeight(h);
52067             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52068             h -= this.el.getBorderWidth("tb");
52069             if(this.config.adjustments){
52070                 h += this.config.adjustments[1];
52071             }
52072             this.bodyEl.setHeight(h);
52073             if(this.tabs){
52074                 h = this.tabs.syncHeight(h);
52075             }
52076         }
52077         if(this.panelSize){
52078             w = w !== null ? w : this.panelSize.width;
52079             h = h !== null ? h : this.panelSize.height;
52080         }
52081         if(this.activePanel){
52082             var el = this.activePanel.getEl();
52083             w = w !== null ? w : el.getWidth();
52084             h = h !== null ? h : el.getHeight();
52085             this.panelSize = {width: w, height: h};
52086             this.activePanel.setSize(w, h);
52087         }
52088         if(Roo.isIE && this.tabs){
52089             this.tabs.el.repaint();
52090         }
52091     },
52092
52093     /**
52094      * Returns the container element for this region.
52095      * @return {Roo.Element}
52096      */
52097     getEl : function(){
52098         return this.el;
52099     },
52100
52101     /**
52102      * Hides this region.
52103      */
52104     hide : function(){
52105         if(!this.collapsed){
52106             this.el.dom.style.left = "-2000px";
52107             this.el.hide();
52108         }else{
52109             this.collapsedEl.dom.style.left = "-2000px";
52110             this.collapsedEl.hide();
52111         }
52112         this.visible = false;
52113         this.fireEvent("visibilitychange", this, false);
52114     },
52115
52116     /**
52117      * Shows this region if it was previously hidden.
52118      */
52119     show : function(){
52120         if(!this.collapsed){
52121             this.el.show();
52122         }else{
52123             this.collapsedEl.show();
52124         }
52125         this.visible = true;
52126         this.fireEvent("visibilitychange", this, true);
52127     },
52128
52129     closeClicked : function(){
52130         if(this.activePanel){
52131             this.remove(this.activePanel);
52132         }
52133     },
52134
52135     collapseClick : function(e){
52136         if(this.isSlid){
52137            e.stopPropagation();
52138            this.slideIn();
52139         }else{
52140            e.stopPropagation();
52141            this.slideOut();
52142         }
52143     },
52144
52145     /**
52146      * Collapses this region.
52147      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52148      */
52149     collapse : function(skipAnim, skipCheck = false){
52150         if(this.collapsed) {
52151             return;
52152         }
52153         
52154         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52155             
52156             this.collapsed = true;
52157             if(this.split){
52158                 this.split.el.hide();
52159             }
52160             if(this.config.animate && skipAnim !== true){
52161                 this.fireEvent("invalidated", this);
52162                 this.animateCollapse();
52163             }else{
52164                 this.el.setLocation(-20000,-20000);
52165                 this.el.hide();
52166                 this.collapsedEl.show();
52167                 this.fireEvent("collapsed", this);
52168                 this.fireEvent("invalidated", this);
52169             }
52170         }
52171         
52172     },
52173
52174     animateCollapse : function(){
52175         // overridden
52176     },
52177
52178     /**
52179      * Expands this region if it was previously collapsed.
52180      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52181      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52182      */
52183     expand : function(e, skipAnim){
52184         if(e) {
52185             e.stopPropagation();
52186         }
52187         if(!this.collapsed || this.el.hasActiveFx()) {
52188             return;
52189         }
52190         if(this.isSlid){
52191             this.afterSlideIn();
52192             skipAnim = true;
52193         }
52194         this.collapsed = false;
52195         if(this.config.animate && skipAnim !== true){
52196             this.animateExpand();
52197         }else{
52198             this.el.show();
52199             if(this.split){
52200                 this.split.el.show();
52201             }
52202             this.collapsedEl.setLocation(-2000,-2000);
52203             this.collapsedEl.hide();
52204             this.fireEvent("invalidated", this);
52205             this.fireEvent("expanded", this);
52206         }
52207     },
52208
52209     animateExpand : function(){
52210         // overridden
52211     },
52212
52213     initTabs : function()
52214     {
52215         this.bodyEl.setStyle("overflow", "hidden");
52216         var ts = new Roo.TabPanel(
52217                 this.bodyEl.dom,
52218                 {
52219                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52220                     disableTooltips: this.config.disableTabTips,
52221                     toolbar : this.config.toolbar
52222                 }
52223         );
52224         if(this.config.hideTabs){
52225             ts.stripWrap.setDisplayed(false);
52226         }
52227         this.tabs = ts;
52228         ts.resizeTabs = this.config.resizeTabs === true;
52229         ts.minTabWidth = this.config.minTabWidth || 40;
52230         ts.maxTabWidth = this.config.maxTabWidth || 250;
52231         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52232         ts.monitorResize = false;
52233         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52234         ts.bodyEl.addClass('x-layout-tabs-body');
52235         this.panels.each(this.initPanelAsTab, this);
52236     },
52237
52238     initPanelAsTab : function(panel){
52239         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52240                     this.config.closeOnTab && panel.isClosable());
52241         if(panel.tabTip !== undefined){
52242             ti.setTooltip(panel.tabTip);
52243         }
52244         ti.on("activate", function(){
52245               this.setActivePanel(panel);
52246         }, this);
52247         if(this.config.closeOnTab){
52248             ti.on("beforeclose", function(t, e){
52249                 e.cancel = true;
52250                 this.remove(panel);
52251             }, this);
52252         }
52253         return ti;
52254     },
52255
52256     updatePanelTitle : function(panel, title){
52257         if(this.activePanel == panel){
52258             this.updateTitle(title);
52259         }
52260         if(this.tabs){
52261             var ti = this.tabs.getTab(panel.getEl().id);
52262             ti.setText(title);
52263             if(panel.tabTip !== undefined){
52264                 ti.setTooltip(panel.tabTip);
52265             }
52266         }
52267     },
52268
52269     updateTitle : function(title){
52270         if(this.titleTextEl && !this.config.title){
52271             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52272         }
52273     },
52274
52275     setActivePanel : function(panel){
52276         panel = this.getPanel(panel);
52277         if(this.activePanel && this.activePanel != panel){
52278             this.activePanel.setActiveState(false);
52279         }
52280         this.activePanel = panel;
52281         panel.setActiveState(true);
52282         if(this.panelSize){
52283             panel.setSize(this.panelSize.width, this.panelSize.height);
52284         }
52285         if(this.closeBtn){
52286             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52287         }
52288         this.updateTitle(panel.getTitle());
52289         if(this.tabs){
52290             this.fireEvent("invalidated", this);
52291         }
52292         this.fireEvent("panelactivated", this, panel);
52293     },
52294
52295     /**
52296      * Shows the specified panel.
52297      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52298      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52299      */
52300     showPanel : function(panel)
52301     {
52302         panel = this.getPanel(panel);
52303         if(panel){
52304             if(this.tabs){
52305                 var tab = this.tabs.getTab(panel.getEl().id);
52306                 if(tab.isHidden()){
52307                     this.tabs.unhideTab(tab.id);
52308                 }
52309                 tab.activate();
52310             }else{
52311                 this.setActivePanel(panel);
52312             }
52313         }
52314         return panel;
52315     },
52316
52317     /**
52318      * Get the active panel for this region.
52319      * @return {Roo.ContentPanel} The active panel or null
52320      */
52321     getActivePanel : function(){
52322         return this.activePanel;
52323     },
52324
52325     validateVisibility : function(){
52326         if(this.panels.getCount() < 1){
52327             this.updateTitle("&#160;");
52328             this.closeBtn.hide();
52329             this.hide();
52330         }else{
52331             if(!this.isVisible()){
52332                 this.show();
52333             }
52334         }
52335     },
52336
52337     /**
52338      * Adds the passed ContentPanel(s) to this region.
52339      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52340      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52341      */
52342     add : function(panel){
52343         if(arguments.length > 1){
52344             for(var i = 0, len = arguments.length; i < len; i++) {
52345                 this.add(arguments[i]);
52346             }
52347             return null;
52348         }
52349         if(this.hasPanel(panel)){
52350             this.showPanel(panel);
52351             return panel;
52352         }
52353         panel.setRegion(this);
52354         this.panels.add(panel);
52355         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52356             this.bodyEl.dom.appendChild(panel.getEl().dom);
52357             if(panel.background !== true){
52358                 this.setActivePanel(panel);
52359             }
52360             this.fireEvent("paneladded", this, panel);
52361             return panel;
52362         }
52363         if(!this.tabs){
52364             this.initTabs();
52365         }else{
52366             this.initPanelAsTab(panel);
52367         }
52368         if(panel.background !== true){
52369             this.tabs.activate(panel.getEl().id);
52370         }
52371         this.fireEvent("paneladded", this, panel);
52372         return panel;
52373     },
52374
52375     /**
52376      * Hides the tab for the specified panel.
52377      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52378      */
52379     hidePanel : function(panel){
52380         if(this.tabs && (panel = this.getPanel(panel))){
52381             this.tabs.hideTab(panel.getEl().id);
52382         }
52383     },
52384
52385     /**
52386      * Unhides the tab for a previously hidden panel.
52387      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52388      */
52389     unhidePanel : function(panel){
52390         if(this.tabs && (panel = this.getPanel(panel))){
52391             this.tabs.unhideTab(panel.getEl().id);
52392         }
52393     },
52394
52395     clearPanels : function(){
52396         while(this.panels.getCount() > 0){
52397              this.remove(this.panels.first());
52398         }
52399     },
52400
52401     /**
52402      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52403      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52404      * @param {Boolean} preservePanel Overrides the config preservePanel option
52405      * @return {Roo.ContentPanel} The panel that was removed
52406      */
52407     remove : function(panel, preservePanel){
52408         panel = this.getPanel(panel);
52409         if(!panel){
52410             return null;
52411         }
52412         var e = {};
52413         this.fireEvent("beforeremove", this, panel, e);
52414         if(e.cancel === true){
52415             return null;
52416         }
52417         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52418         var panelId = panel.getId();
52419         this.panels.removeKey(panelId);
52420         if(preservePanel){
52421             document.body.appendChild(panel.getEl().dom);
52422         }
52423         if(this.tabs){
52424             this.tabs.removeTab(panel.getEl().id);
52425         }else if (!preservePanel){
52426             this.bodyEl.dom.removeChild(panel.getEl().dom);
52427         }
52428         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52429             var p = this.panels.first();
52430             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52431             tempEl.appendChild(p.getEl().dom);
52432             this.bodyEl.update("");
52433             this.bodyEl.dom.appendChild(p.getEl().dom);
52434             tempEl = null;
52435             this.updateTitle(p.getTitle());
52436             this.tabs = null;
52437             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52438             this.setActivePanel(p);
52439         }
52440         panel.setRegion(null);
52441         if(this.activePanel == panel){
52442             this.activePanel = null;
52443         }
52444         if(this.config.autoDestroy !== false && preservePanel !== true){
52445             try{panel.destroy();}catch(e){}
52446         }
52447         this.fireEvent("panelremoved", this, panel);
52448         return panel;
52449     },
52450
52451     /**
52452      * Returns the TabPanel component used by this region
52453      * @return {Roo.TabPanel}
52454      */
52455     getTabs : function(){
52456         return this.tabs;
52457     },
52458
52459     createTool : function(parentEl, className){
52460         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52461             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52462         btn.addClassOnOver("x-layout-tools-button-over");
52463         return btn;
52464     }
52465 });/*
52466  * Based on:
52467  * Ext JS Library 1.1.1
52468  * Copyright(c) 2006-2007, Ext JS, LLC.
52469  *
52470  * Originally Released Under LGPL - original licence link has changed is not relivant.
52471  *
52472  * Fork - LGPL
52473  * <script type="text/javascript">
52474  */
52475  
52476
52477
52478 /**
52479  * @class Roo.SplitLayoutRegion
52480  * @extends Roo.LayoutRegion
52481  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52482  */
52483 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52484     this.cursor = cursor;
52485     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52486 };
52487
52488 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52489     splitTip : "Drag to resize.",
52490     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52491     useSplitTips : false,
52492
52493     applyConfig : function(config){
52494         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52495         if(config.split){
52496             if(!this.split){
52497                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52498                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52499                 /** The SplitBar for this region 
52500                 * @type Roo.SplitBar */
52501                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52502                 this.split.on("moved", this.onSplitMove, this);
52503                 this.split.useShim = config.useShim === true;
52504                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52505                 if(this.useSplitTips){
52506                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52507                 }
52508                 if(config.collapsible){
52509                     this.split.el.on("dblclick", this.collapse,  this);
52510                 }
52511             }
52512             if(typeof config.minSize != "undefined"){
52513                 this.split.minSize = config.minSize;
52514             }
52515             if(typeof config.maxSize != "undefined"){
52516                 this.split.maxSize = config.maxSize;
52517             }
52518             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52519                 this.hideSplitter();
52520             }
52521         }
52522     },
52523
52524     getHMaxSize : function(){
52525          var cmax = this.config.maxSize || 10000;
52526          var center = this.mgr.getRegion("center");
52527          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52528     },
52529
52530     getVMaxSize : function(){
52531          var cmax = this.config.maxSize || 10000;
52532          var center = this.mgr.getRegion("center");
52533          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52534     },
52535
52536     onSplitMove : function(split, newSize){
52537         this.fireEvent("resized", this, newSize);
52538     },
52539     
52540     /** 
52541      * Returns the {@link Roo.SplitBar} for this region.
52542      * @return {Roo.SplitBar}
52543      */
52544     getSplitBar : function(){
52545         return this.split;
52546     },
52547     
52548     hide : function(){
52549         this.hideSplitter();
52550         Roo.SplitLayoutRegion.superclass.hide.call(this);
52551     },
52552
52553     hideSplitter : function(){
52554         if(this.split){
52555             this.split.el.setLocation(-2000,-2000);
52556             this.split.el.hide();
52557         }
52558     },
52559
52560     show : function(){
52561         if(this.split){
52562             this.split.el.show();
52563         }
52564         Roo.SplitLayoutRegion.superclass.show.call(this);
52565     },
52566     
52567     beforeSlide: function(){
52568         if(Roo.isGecko){// firefox overflow auto bug workaround
52569             this.bodyEl.clip();
52570             if(this.tabs) {
52571                 this.tabs.bodyEl.clip();
52572             }
52573             if(this.activePanel){
52574                 this.activePanel.getEl().clip();
52575                 
52576                 if(this.activePanel.beforeSlide){
52577                     this.activePanel.beforeSlide();
52578                 }
52579             }
52580         }
52581     },
52582     
52583     afterSlide : function(){
52584         if(Roo.isGecko){// firefox overflow auto bug workaround
52585             this.bodyEl.unclip();
52586             if(this.tabs) {
52587                 this.tabs.bodyEl.unclip();
52588             }
52589             if(this.activePanel){
52590                 this.activePanel.getEl().unclip();
52591                 if(this.activePanel.afterSlide){
52592                     this.activePanel.afterSlide();
52593                 }
52594             }
52595         }
52596     },
52597
52598     initAutoHide : function(){
52599         if(this.autoHide !== false){
52600             if(!this.autoHideHd){
52601                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52602                 this.autoHideHd = {
52603                     "mouseout": function(e){
52604                         if(!e.within(this.el, true)){
52605                             st.delay(500);
52606                         }
52607                     },
52608                     "mouseover" : function(e){
52609                         st.cancel();
52610                     },
52611                     scope : this
52612                 };
52613             }
52614             this.el.on(this.autoHideHd);
52615         }
52616     },
52617
52618     clearAutoHide : function(){
52619         if(this.autoHide !== false){
52620             this.el.un("mouseout", this.autoHideHd.mouseout);
52621             this.el.un("mouseover", this.autoHideHd.mouseover);
52622         }
52623     },
52624
52625     clearMonitor : function(){
52626         Roo.get(document).un("click", this.slideInIf, this);
52627     },
52628
52629     // these names are backwards but not changed for compat
52630     slideOut : function(){
52631         if(this.isSlid || this.el.hasActiveFx()){
52632             return;
52633         }
52634         this.isSlid = true;
52635         if(this.collapseBtn){
52636             this.collapseBtn.hide();
52637         }
52638         this.closeBtnState = this.closeBtn.getStyle('display');
52639         this.closeBtn.hide();
52640         if(this.stickBtn){
52641             this.stickBtn.show();
52642         }
52643         this.el.show();
52644         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52645         this.beforeSlide();
52646         this.el.setStyle("z-index", 10001);
52647         this.el.slideIn(this.getSlideAnchor(), {
52648             callback: function(){
52649                 this.afterSlide();
52650                 this.initAutoHide();
52651                 Roo.get(document).on("click", this.slideInIf, this);
52652                 this.fireEvent("slideshow", this);
52653             },
52654             scope: this,
52655             block: true
52656         });
52657     },
52658
52659     afterSlideIn : function(){
52660         this.clearAutoHide();
52661         this.isSlid = false;
52662         this.clearMonitor();
52663         this.el.setStyle("z-index", "");
52664         if(this.collapseBtn){
52665             this.collapseBtn.show();
52666         }
52667         this.closeBtn.setStyle('display', this.closeBtnState);
52668         if(this.stickBtn){
52669             this.stickBtn.hide();
52670         }
52671         this.fireEvent("slidehide", this);
52672     },
52673
52674     slideIn : function(cb){
52675         if(!this.isSlid || this.el.hasActiveFx()){
52676             Roo.callback(cb);
52677             return;
52678         }
52679         this.isSlid = false;
52680         this.beforeSlide();
52681         this.el.slideOut(this.getSlideAnchor(), {
52682             callback: function(){
52683                 this.el.setLeftTop(-10000, -10000);
52684                 this.afterSlide();
52685                 this.afterSlideIn();
52686                 Roo.callback(cb);
52687             },
52688             scope: this,
52689             block: true
52690         });
52691     },
52692     
52693     slideInIf : function(e){
52694         if(!e.within(this.el)){
52695             this.slideIn();
52696         }
52697     },
52698
52699     animateCollapse : function(){
52700         this.beforeSlide();
52701         this.el.setStyle("z-index", 20000);
52702         var anchor = this.getSlideAnchor();
52703         this.el.slideOut(anchor, {
52704             callback : function(){
52705                 this.el.setStyle("z-index", "");
52706                 this.collapsedEl.slideIn(anchor, {duration:.3});
52707                 this.afterSlide();
52708                 this.el.setLocation(-10000,-10000);
52709                 this.el.hide();
52710                 this.fireEvent("collapsed", this);
52711             },
52712             scope: this,
52713             block: true
52714         });
52715     },
52716
52717     animateExpand : function(){
52718         this.beforeSlide();
52719         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52720         this.el.setStyle("z-index", 20000);
52721         this.collapsedEl.hide({
52722             duration:.1
52723         });
52724         this.el.slideIn(this.getSlideAnchor(), {
52725             callback : function(){
52726                 this.el.setStyle("z-index", "");
52727                 this.afterSlide();
52728                 if(this.split){
52729                     this.split.el.show();
52730                 }
52731                 this.fireEvent("invalidated", this);
52732                 this.fireEvent("expanded", this);
52733             },
52734             scope: this,
52735             block: true
52736         });
52737     },
52738
52739     anchors : {
52740         "west" : "left",
52741         "east" : "right",
52742         "north" : "top",
52743         "south" : "bottom"
52744     },
52745
52746     sanchors : {
52747         "west" : "l",
52748         "east" : "r",
52749         "north" : "t",
52750         "south" : "b"
52751     },
52752
52753     canchors : {
52754         "west" : "tl-tr",
52755         "east" : "tr-tl",
52756         "north" : "tl-bl",
52757         "south" : "bl-tl"
52758     },
52759
52760     getAnchor : function(){
52761         return this.anchors[this.position];
52762     },
52763
52764     getCollapseAnchor : function(){
52765         return this.canchors[this.position];
52766     },
52767
52768     getSlideAnchor : function(){
52769         return this.sanchors[this.position];
52770     },
52771
52772     getAlignAdj : function(){
52773         var cm = this.cmargins;
52774         switch(this.position){
52775             case "west":
52776                 return [0, 0];
52777             break;
52778             case "east":
52779                 return [0, 0];
52780             break;
52781             case "north":
52782                 return [0, 0];
52783             break;
52784             case "south":
52785                 return [0, 0];
52786             break;
52787         }
52788     },
52789
52790     getExpandAdj : function(){
52791         var c = this.collapsedEl, cm = this.cmargins;
52792         switch(this.position){
52793             case "west":
52794                 return [-(cm.right+c.getWidth()+cm.left), 0];
52795             break;
52796             case "east":
52797                 return [cm.right+c.getWidth()+cm.left, 0];
52798             break;
52799             case "north":
52800                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52801             break;
52802             case "south":
52803                 return [0, cm.top+cm.bottom+c.getHeight()];
52804             break;
52805         }
52806     }
52807 });/*
52808  * Based on:
52809  * Ext JS Library 1.1.1
52810  * Copyright(c) 2006-2007, Ext JS, LLC.
52811  *
52812  * Originally Released Under LGPL - original licence link has changed is not relivant.
52813  *
52814  * Fork - LGPL
52815  * <script type="text/javascript">
52816  */
52817 /*
52818  * These classes are private internal classes
52819  */
52820 Roo.CenterLayoutRegion = function(mgr, config){
52821     Roo.LayoutRegion.call(this, mgr, config, "center");
52822     this.visible = true;
52823     this.minWidth = config.minWidth || 20;
52824     this.minHeight = config.minHeight || 20;
52825 };
52826
52827 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52828     hide : function(){
52829         // center panel can't be hidden
52830     },
52831     
52832     show : function(){
52833         // center panel can't be hidden
52834     },
52835     
52836     getMinWidth: function(){
52837         return this.minWidth;
52838     },
52839     
52840     getMinHeight: function(){
52841         return this.minHeight;
52842     }
52843 });
52844
52845
52846 Roo.NorthLayoutRegion = function(mgr, config){
52847     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52848     if(this.split){
52849         this.split.placement = Roo.SplitBar.TOP;
52850         this.split.orientation = Roo.SplitBar.VERTICAL;
52851         this.split.el.addClass("x-layout-split-v");
52852     }
52853     var size = config.initialSize || config.height;
52854     if(typeof size != "undefined"){
52855         this.el.setHeight(size);
52856     }
52857 };
52858 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52859     orientation: Roo.SplitBar.VERTICAL,
52860     getBox : function(){
52861         if(this.collapsed){
52862             return this.collapsedEl.getBox();
52863         }
52864         var box = this.el.getBox();
52865         if(this.split){
52866             box.height += this.split.el.getHeight();
52867         }
52868         return box;
52869     },
52870     
52871     updateBox : function(box){
52872         if(this.split && !this.collapsed){
52873             box.height -= this.split.el.getHeight();
52874             this.split.el.setLeft(box.x);
52875             this.split.el.setTop(box.y+box.height);
52876             this.split.el.setWidth(box.width);
52877         }
52878         if(this.collapsed){
52879             this.updateBody(box.width, null);
52880         }
52881         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52882     }
52883 });
52884
52885 Roo.SouthLayoutRegion = function(mgr, config){
52886     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
52887     if(this.split){
52888         this.split.placement = Roo.SplitBar.BOTTOM;
52889         this.split.orientation = Roo.SplitBar.VERTICAL;
52890         this.split.el.addClass("x-layout-split-v");
52891     }
52892     var size = config.initialSize || config.height;
52893     if(typeof size != "undefined"){
52894         this.el.setHeight(size);
52895     }
52896 };
52897 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
52898     orientation: Roo.SplitBar.VERTICAL,
52899     getBox : function(){
52900         if(this.collapsed){
52901             return this.collapsedEl.getBox();
52902         }
52903         var box = this.el.getBox();
52904         if(this.split){
52905             var sh = this.split.el.getHeight();
52906             box.height += sh;
52907             box.y -= sh;
52908         }
52909         return box;
52910     },
52911     
52912     updateBox : function(box){
52913         if(this.split && !this.collapsed){
52914             var sh = this.split.el.getHeight();
52915             box.height -= sh;
52916             box.y += sh;
52917             this.split.el.setLeft(box.x);
52918             this.split.el.setTop(box.y-sh);
52919             this.split.el.setWidth(box.width);
52920         }
52921         if(this.collapsed){
52922             this.updateBody(box.width, null);
52923         }
52924         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52925     }
52926 });
52927
52928 Roo.EastLayoutRegion = function(mgr, config){
52929     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
52930     if(this.split){
52931         this.split.placement = Roo.SplitBar.RIGHT;
52932         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52933         this.split.el.addClass("x-layout-split-h");
52934     }
52935     var size = config.initialSize || config.width;
52936     if(typeof size != "undefined"){
52937         this.el.setWidth(size);
52938     }
52939 };
52940 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
52941     orientation: Roo.SplitBar.HORIZONTAL,
52942     getBox : function(){
52943         if(this.collapsed){
52944             return this.collapsedEl.getBox();
52945         }
52946         var box = this.el.getBox();
52947         if(this.split){
52948             var sw = this.split.el.getWidth();
52949             box.width += sw;
52950             box.x -= sw;
52951         }
52952         return box;
52953     },
52954
52955     updateBox : function(box){
52956         if(this.split && !this.collapsed){
52957             var sw = this.split.el.getWidth();
52958             box.width -= sw;
52959             this.split.el.setLeft(box.x);
52960             this.split.el.setTop(box.y);
52961             this.split.el.setHeight(box.height);
52962             box.x += sw;
52963         }
52964         if(this.collapsed){
52965             this.updateBody(null, box.height);
52966         }
52967         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52968     }
52969 });
52970
52971 Roo.WestLayoutRegion = function(mgr, config){
52972     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
52973     if(this.split){
52974         this.split.placement = Roo.SplitBar.LEFT;
52975         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52976         this.split.el.addClass("x-layout-split-h");
52977     }
52978     var size = config.initialSize || config.width;
52979     if(typeof size != "undefined"){
52980         this.el.setWidth(size);
52981     }
52982 };
52983 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
52984     orientation: Roo.SplitBar.HORIZONTAL,
52985     getBox : function(){
52986         if(this.collapsed){
52987             return this.collapsedEl.getBox();
52988         }
52989         var box = this.el.getBox();
52990         if(this.split){
52991             box.width += this.split.el.getWidth();
52992         }
52993         return box;
52994     },
52995     
52996     updateBox : function(box){
52997         if(this.split && !this.collapsed){
52998             var sw = this.split.el.getWidth();
52999             box.width -= sw;
53000             this.split.el.setLeft(box.x+box.width);
53001             this.split.el.setTop(box.y);
53002             this.split.el.setHeight(box.height);
53003         }
53004         if(this.collapsed){
53005             this.updateBody(null, box.height);
53006         }
53007         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53008     }
53009 });
53010 /*
53011  * Based on:
53012  * Ext JS Library 1.1.1
53013  * Copyright(c) 2006-2007, Ext JS, LLC.
53014  *
53015  * Originally Released Under LGPL - original licence link has changed is not relivant.
53016  *
53017  * Fork - LGPL
53018  * <script type="text/javascript">
53019  */
53020  
53021  
53022 /*
53023  * Private internal class for reading and applying state
53024  */
53025 Roo.LayoutStateManager = function(layout){
53026      // default empty state
53027      this.state = {
53028         north: {},
53029         south: {},
53030         east: {},
53031         west: {}       
53032     };
53033 };
53034
53035 Roo.LayoutStateManager.prototype = {
53036     init : function(layout, provider){
53037         this.provider = provider;
53038         var state = provider.get(layout.id+"-layout-state");
53039         if(state){
53040             var wasUpdating = layout.isUpdating();
53041             if(!wasUpdating){
53042                 layout.beginUpdate();
53043             }
53044             for(var key in state){
53045                 if(typeof state[key] != "function"){
53046                     var rstate = state[key];
53047                     var r = layout.getRegion(key);
53048                     if(r && rstate){
53049                         if(rstate.size){
53050                             r.resizeTo(rstate.size);
53051                         }
53052                         if(rstate.collapsed == true){
53053                             r.collapse(true);
53054                         }else{
53055                             r.expand(null, true);
53056                         }
53057                     }
53058                 }
53059             }
53060             if(!wasUpdating){
53061                 layout.endUpdate();
53062             }
53063             this.state = state; 
53064         }
53065         this.layout = layout;
53066         layout.on("regionresized", this.onRegionResized, this);
53067         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53068         layout.on("regionexpanded", this.onRegionExpanded, this);
53069     },
53070     
53071     storeState : function(){
53072         this.provider.set(this.layout.id+"-layout-state", this.state);
53073     },
53074     
53075     onRegionResized : function(region, newSize){
53076         this.state[region.getPosition()].size = newSize;
53077         this.storeState();
53078     },
53079     
53080     onRegionCollapsed : function(region){
53081         this.state[region.getPosition()].collapsed = true;
53082         this.storeState();
53083     },
53084     
53085     onRegionExpanded : function(region){
53086         this.state[region.getPosition()].collapsed = false;
53087         this.storeState();
53088     }
53089 };/*
53090  * Based on:
53091  * Ext JS Library 1.1.1
53092  * Copyright(c) 2006-2007, Ext JS, LLC.
53093  *
53094  * Originally Released Under LGPL - original licence link has changed is not relivant.
53095  *
53096  * Fork - LGPL
53097  * <script type="text/javascript">
53098  */
53099 /**
53100  * @class Roo.ContentPanel
53101  * @extends Roo.util.Observable
53102  * A basic ContentPanel element.
53103  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53104  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53105  * @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
53106  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53107  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53108  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53109  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53110  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53111  * @cfg {String} title          The title for this panel
53112  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53113  * @cfg {String} url            Calls {@link #setUrl} with this value
53114  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53115  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53116  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53117  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53118
53119  * @constructor
53120  * Create a new ContentPanel.
53121  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53122  * @param {String/Object} config A string to set only the title or a config object
53123  * @param {String} content (optional) Set the HTML content for this panel
53124  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53125  */
53126 Roo.ContentPanel = function(el, config, content){
53127     
53128      
53129     /*
53130     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53131         config = el;
53132         el = Roo.id();
53133     }
53134     if (config && config.parentLayout) { 
53135         el = config.parentLayout.el.createChild(); 
53136     }
53137     */
53138     if(el.autoCreate){ // xtype is available if this is called from factory
53139         config = el;
53140         el = Roo.id();
53141     }
53142     this.el = Roo.get(el);
53143     if(!this.el && config && config.autoCreate){
53144         if(typeof config.autoCreate == "object"){
53145             if(!config.autoCreate.id){
53146                 config.autoCreate.id = config.id||el;
53147             }
53148             this.el = Roo.DomHelper.append(document.body,
53149                         config.autoCreate, true);
53150         }else{
53151             this.el = Roo.DomHelper.append(document.body,
53152                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53153         }
53154     }
53155     this.closable = false;
53156     this.loaded = false;
53157     this.active = false;
53158     if(typeof config == "string"){
53159         this.title = config;
53160     }else{
53161         Roo.apply(this, config);
53162     }
53163     
53164     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53165         this.wrapEl = this.el.wrap();
53166         this.toolbar.container = this.el.insertSibling(false, 'before');
53167         this.toolbar = new Roo.Toolbar(this.toolbar);
53168     }
53169     
53170     // xtype created footer. - not sure if will work as we normally have to render first..
53171     if (this.footer && !this.footer.el && this.footer.xtype) {
53172         if (!this.wrapEl) {
53173             this.wrapEl = this.el.wrap();
53174         }
53175     
53176         this.footer.container = this.wrapEl.createChild();
53177          
53178         this.footer = Roo.factory(this.footer, Roo);
53179         
53180     }
53181     
53182     if(this.resizeEl){
53183         this.resizeEl = Roo.get(this.resizeEl, true);
53184     }else{
53185         this.resizeEl = this.el;
53186     }
53187     // handle view.xtype
53188     
53189  
53190     
53191     
53192     this.addEvents({
53193         /**
53194          * @event activate
53195          * Fires when this panel is activated. 
53196          * @param {Roo.ContentPanel} this
53197          */
53198         "activate" : true,
53199         /**
53200          * @event deactivate
53201          * Fires when this panel is activated. 
53202          * @param {Roo.ContentPanel} this
53203          */
53204         "deactivate" : true,
53205
53206         /**
53207          * @event resize
53208          * Fires when this panel is resized if fitToFrame is true.
53209          * @param {Roo.ContentPanel} this
53210          * @param {Number} width The width after any component adjustments
53211          * @param {Number} height The height after any component adjustments
53212          */
53213         "resize" : true,
53214         
53215          /**
53216          * @event render
53217          * Fires when this tab is created
53218          * @param {Roo.ContentPanel} this
53219          */
53220         "render" : true
53221         
53222         
53223         
53224     });
53225     
53226
53227     
53228     
53229     if(this.autoScroll){
53230         this.resizeEl.setStyle("overflow", "auto");
53231     } else {
53232         // fix randome scrolling
53233         this.el.on('scroll', function() {
53234             Roo.log('fix random scolling');
53235             this.scrollTo('top',0); 
53236         });
53237     }
53238     content = content || this.content;
53239     if(content){
53240         this.setContent(content);
53241     }
53242     if(config && config.url){
53243         this.setUrl(this.url, this.params, this.loadOnce);
53244     }
53245     
53246     
53247     
53248     Roo.ContentPanel.superclass.constructor.call(this);
53249     
53250     if (this.view && typeof(this.view.xtype) != 'undefined') {
53251         this.view.el = this.el.appendChild(document.createElement("div"));
53252         this.view = Roo.factory(this.view); 
53253         this.view.render  &&  this.view.render(false, '');  
53254     }
53255     
53256     
53257     this.fireEvent('render', this);
53258 };
53259
53260 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53261     tabTip:'',
53262     setRegion : function(region){
53263         this.region = region;
53264         if(region){
53265            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53266         }else{
53267            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53268         } 
53269     },
53270     
53271     /**
53272      * Returns the toolbar for this Panel if one was configured. 
53273      * @return {Roo.Toolbar} 
53274      */
53275     getToolbar : function(){
53276         return this.toolbar;
53277     },
53278     
53279     setActiveState : function(active){
53280         this.active = active;
53281         if(!active){
53282             this.fireEvent("deactivate", this);
53283         }else{
53284             this.fireEvent("activate", this);
53285         }
53286     },
53287     /**
53288      * Updates this panel's element
53289      * @param {String} content The new content
53290      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53291     */
53292     setContent : function(content, loadScripts){
53293         this.el.update(content, loadScripts);
53294     },
53295
53296     ignoreResize : function(w, h){
53297         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53298             return true;
53299         }else{
53300             this.lastSize = {width: w, height: h};
53301             return false;
53302         }
53303     },
53304     /**
53305      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53306      * @return {Roo.UpdateManager} The UpdateManager
53307      */
53308     getUpdateManager : function(){
53309         return this.el.getUpdateManager();
53310     },
53311      /**
53312      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53313      * @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:
53314 <pre><code>
53315 panel.load({
53316     url: "your-url.php",
53317     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53318     callback: yourFunction,
53319     scope: yourObject, //(optional scope)
53320     discardUrl: false,
53321     nocache: false,
53322     text: "Loading...",
53323     timeout: 30,
53324     scripts: false
53325 });
53326 </code></pre>
53327      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53328      * 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.
53329      * @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}
53330      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53331      * @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.
53332      * @return {Roo.ContentPanel} this
53333      */
53334     load : function(){
53335         var um = this.el.getUpdateManager();
53336         um.update.apply(um, arguments);
53337         return this;
53338     },
53339
53340
53341     /**
53342      * 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.
53343      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53344      * @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)
53345      * @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)
53346      * @return {Roo.UpdateManager} The UpdateManager
53347      */
53348     setUrl : function(url, params, loadOnce){
53349         if(this.refreshDelegate){
53350             this.removeListener("activate", this.refreshDelegate);
53351         }
53352         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53353         this.on("activate", this.refreshDelegate);
53354         return this.el.getUpdateManager();
53355     },
53356     
53357     _handleRefresh : function(url, params, loadOnce){
53358         if(!loadOnce || !this.loaded){
53359             var updater = this.el.getUpdateManager();
53360             updater.update(url, params, this._setLoaded.createDelegate(this));
53361         }
53362     },
53363     
53364     _setLoaded : function(){
53365         this.loaded = true;
53366     }, 
53367     
53368     /**
53369      * Returns this panel's id
53370      * @return {String} 
53371      */
53372     getId : function(){
53373         return this.el.id;
53374     },
53375     
53376     /** 
53377      * Returns this panel's element - used by regiosn to add.
53378      * @return {Roo.Element} 
53379      */
53380     getEl : function(){
53381         return this.wrapEl || this.el;
53382     },
53383     
53384     adjustForComponents : function(width, height)
53385     {
53386         //Roo.log('adjustForComponents ');
53387         if(this.resizeEl != this.el){
53388             width -= this.el.getFrameWidth('lr');
53389             height -= this.el.getFrameWidth('tb');
53390         }
53391         if(this.toolbar){
53392             var te = this.toolbar.getEl();
53393             height -= te.getHeight();
53394             te.setWidth(width);
53395         }
53396         if(this.footer){
53397             var te = this.footer.getEl();
53398             Roo.log("footer:" + te.getHeight());
53399             
53400             height -= te.getHeight();
53401             te.setWidth(width);
53402         }
53403         
53404         
53405         if(this.adjustments){
53406             width += this.adjustments[0];
53407             height += this.adjustments[1];
53408         }
53409         return {"width": width, "height": height};
53410     },
53411     
53412     setSize : function(width, height){
53413         if(this.fitToFrame && !this.ignoreResize(width, height)){
53414             if(this.fitContainer && this.resizeEl != this.el){
53415                 this.el.setSize(width, height);
53416             }
53417             var size = this.adjustForComponents(width, height);
53418             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53419             this.fireEvent('resize', this, size.width, size.height);
53420         }
53421     },
53422     
53423     /**
53424      * Returns this panel's title
53425      * @return {String} 
53426      */
53427     getTitle : function(){
53428         return this.title;
53429     },
53430     
53431     /**
53432      * Set this panel's title
53433      * @param {String} title
53434      */
53435     setTitle : function(title){
53436         this.title = title;
53437         if(this.region){
53438             this.region.updatePanelTitle(this, title);
53439         }
53440     },
53441     
53442     /**
53443      * Returns true is this panel was configured to be closable
53444      * @return {Boolean} 
53445      */
53446     isClosable : function(){
53447         return this.closable;
53448     },
53449     
53450     beforeSlide : function(){
53451         this.el.clip();
53452         this.resizeEl.clip();
53453     },
53454     
53455     afterSlide : function(){
53456         this.el.unclip();
53457         this.resizeEl.unclip();
53458     },
53459     
53460     /**
53461      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53462      *   Will fail silently if the {@link #setUrl} method has not been called.
53463      *   This does not activate the panel, just updates its content.
53464      */
53465     refresh : function(){
53466         if(this.refreshDelegate){
53467            this.loaded = false;
53468            this.refreshDelegate();
53469         }
53470     },
53471     
53472     /**
53473      * Destroys this panel
53474      */
53475     destroy : function(){
53476         this.el.removeAllListeners();
53477         var tempEl = document.createElement("span");
53478         tempEl.appendChild(this.el.dom);
53479         tempEl.innerHTML = "";
53480         this.el.remove();
53481         this.el = null;
53482     },
53483     
53484     /**
53485      * form - if the content panel contains a form - this is a reference to it.
53486      * @type {Roo.form.Form}
53487      */
53488     form : false,
53489     /**
53490      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53491      *    This contains a reference to it.
53492      * @type {Roo.View}
53493      */
53494     view : false,
53495     
53496       /**
53497      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53498      * <pre><code>
53499
53500 layout.addxtype({
53501        xtype : 'Form',
53502        items: [ .... ]
53503    }
53504 );
53505
53506 </code></pre>
53507      * @param {Object} cfg Xtype definition of item to add.
53508      */
53509     
53510     addxtype : function(cfg) {
53511         // add form..
53512         if (cfg.xtype.match(/^Form$/)) {
53513             
53514             var el;
53515             //if (this.footer) {
53516             //    el = this.footer.container.insertSibling(false, 'before');
53517             //} else {
53518                 el = this.el.createChild();
53519             //}
53520
53521             this.form = new  Roo.form.Form(cfg);
53522             
53523             
53524             if ( this.form.allItems.length) {
53525                 this.form.render(el.dom);
53526             }
53527             return this.form;
53528         }
53529         // should only have one of theses..
53530         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53531             // views.. should not be just added - used named prop 'view''
53532             
53533             cfg.el = this.el.appendChild(document.createElement("div"));
53534             // factory?
53535             
53536             var ret = new Roo.factory(cfg);
53537              
53538              ret.render && ret.render(false, ''); // render blank..
53539             this.view = ret;
53540             return ret;
53541         }
53542         return false;
53543     }
53544 });
53545
53546 /**
53547  * @class Roo.GridPanel
53548  * @extends Roo.ContentPanel
53549  * @constructor
53550  * Create a new GridPanel.
53551  * @param {Roo.grid.Grid} grid The grid for this panel
53552  * @param {String/Object} config A string to set only the panel's title, or a config object
53553  */
53554 Roo.GridPanel = function(grid, config){
53555     
53556   
53557     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53558         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53559         
53560     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53561     
53562     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53563     
53564     if(this.toolbar){
53565         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53566     }
53567     // xtype created footer. - not sure if will work as we normally have to render first..
53568     if (this.footer && !this.footer.el && this.footer.xtype) {
53569         
53570         this.footer.container = this.grid.getView().getFooterPanel(true);
53571         this.footer.dataSource = this.grid.dataSource;
53572         this.footer = Roo.factory(this.footer, Roo);
53573         
53574     }
53575     
53576     grid.monitorWindowResize = false; // turn off autosizing
53577     grid.autoHeight = false;
53578     grid.autoWidth = false;
53579     this.grid = grid;
53580     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53581 };
53582
53583 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53584     getId : function(){
53585         return this.grid.id;
53586     },
53587     
53588     /**
53589      * Returns the grid for this panel
53590      * @return {Roo.grid.Grid} 
53591      */
53592     getGrid : function(){
53593         return this.grid;    
53594     },
53595     
53596     setSize : function(width, height){
53597         if(!this.ignoreResize(width, height)){
53598             var grid = this.grid;
53599             var size = this.adjustForComponents(width, height);
53600             grid.getGridEl().setSize(size.width, size.height);
53601             grid.autoSize();
53602         }
53603     },
53604     
53605     beforeSlide : function(){
53606         this.grid.getView().scroller.clip();
53607     },
53608     
53609     afterSlide : function(){
53610         this.grid.getView().scroller.unclip();
53611     },
53612     
53613     destroy : function(){
53614         this.grid.destroy();
53615         delete this.grid;
53616         Roo.GridPanel.superclass.destroy.call(this); 
53617     }
53618 });
53619
53620
53621 /**
53622  * @class Roo.NestedLayoutPanel
53623  * @extends Roo.ContentPanel
53624  * @constructor
53625  * Create a new NestedLayoutPanel.
53626  * 
53627  * 
53628  * @param {Roo.BorderLayout} layout The layout for this panel
53629  * @param {String/Object} config A string to set only the title or a config object
53630  */
53631 Roo.NestedLayoutPanel = function(layout, config)
53632 {
53633     // construct with only one argument..
53634     /* FIXME - implement nicer consturctors
53635     if (layout.layout) {
53636         config = layout;
53637         layout = config.layout;
53638         delete config.layout;
53639     }
53640     if (layout.xtype && !layout.getEl) {
53641         // then layout needs constructing..
53642         layout = Roo.factory(layout, Roo);
53643     }
53644     */
53645     
53646     
53647     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53648     
53649     layout.monitorWindowResize = false; // turn off autosizing
53650     this.layout = layout;
53651     this.layout.getEl().addClass("x-layout-nested-layout");
53652     
53653     
53654     
53655     
53656 };
53657
53658 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53659
53660     setSize : function(width, height){
53661         if(!this.ignoreResize(width, height)){
53662             var size = this.adjustForComponents(width, height);
53663             var el = this.layout.getEl();
53664             el.setSize(size.width, size.height);
53665             var touch = el.dom.offsetWidth;
53666             this.layout.layout();
53667             // ie requires a double layout on the first pass
53668             if(Roo.isIE && !this.initialized){
53669                 this.initialized = true;
53670                 this.layout.layout();
53671             }
53672         }
53673     },
53674     
53675     // activate all subpanels if not currently active..
53676     
53677     setActiveState : function(active){
53678         this.active = active;
53679         if(!active){
53680             this.fireEvent("deactivate", this);
53681             return;
53682         }
53683         
53684         this.fireEvent("activate", this);
53685         // not sure if this should happen before or after..
53686         if (!this.layout) {
53687             return; // should not happen..
53688         }
53689         var reg = false;
53690         for (var r in this.layout.regions) {
53691             reg = this.layout.getRegion(r);
53692             if (reg.getActivePanel()) {
53693                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53694                 reg.setActivePanel(reg.getActivePanel());
53695                 continue;
53696             }
53697             if (!reg.panels.length) {
53698                 continue;
53699             }
53700             reg.showPanel(reg.getPanel(0));
53701         }
53702         
53703         
53704         
53705         
53706     },
53707     
53708     /**
53709      * Returns the nested BorderLayout for this panel
53710      * @return {Roo.BorderLayout} 
53711      */
53712     getLayout : function(){
53713         return this.layout;
53714     },
53715     
53716      /**
53717      * Adds a xtype elements to the layout of the nested panel
53718      * <pre><code>
53719
53720 panel.addxtype({
53721        xtype : 'ContentPanel',
53722        region: 'west',
53723        items: [ .... ]
53724    }
53725 );
53726
53727 panel.addxtype({
53728         xtype : 'NestedLayoutPanel',
53729         region: 'west',
53730         layout: {
53731            center: { },
53732            west: { }   
53733         },
53734         items : [ ... list of content panels or nested layout panels.. ]
53735    }
53736 );
53737 </code></pre>
53738      * @param {Object} cfg Xtype definition of item to add.
53739      */
53740     addxtype : function(cfg) {
53741         return this.layout.addxtype(cfg);
53742     
53743     }
53744 });
53745
53746 Roo.ScrollPanel = function(el, config, content){
53747     config = config || {};
53748     config.fitToFrame = true;
53749     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53750     
53751     this.el.dom.style.overflow = "hidden";
53752     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53753     this.el.removeClass("x-layout-inactive-content");
53754     this.el.on("mousewheel", this.onWheel, this);
53755
53756     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53757     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53758     up.unselectable(); down.unselectable();
53759     up.on("click", this.scrollUp, this);
53760     down.on("click", this.scrollDown, this);
53761     up.addClassOnOver("x-scroller-btn-over");
53762     down.addClassOnOver("x-scroller-btn-over");
53763     up.addClassOnClick("x-scroller-btn-click");
53764     down.addClassOnClick("x-scroller-btn-click");
53765     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53766
53767     this.resizeEl = this.el;
53768     this.el = wrap; this.up = up; this.down = down;
53769 };
53770
53771 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53772     increment : 100,
53773     wheelIncrement : 5,
53774     scrollUp : function(){
53775         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53776     },
53777
53778     scrollDown : function(){
53779         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53780     },
53781
53782     afterScroll : function(){
53783         var el = this.resizeEl;
53784         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53785         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53786         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53787     },
53788
53789     setSize : function(){
53790         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53791         this.afterScroll();
53792     },
53793
53794     onWheel : function(e){
53795         var d = e.getWheelDelta();
53796         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53797         this.afterScroll();
53798         e.stopEvent();
53799     },
53800
53801     setContent : function(content, loadScripts){
53802         this.resizeEl.update(content, loadScripts);
53803     }
53804
53805 });
53806
53807
53808
53809
53810
53811
53812
53813
53814
53815 /**
53816  * @class Roo.TreePanel
53817  * @extends Roo.ContentPanel
53818  * @constructor
53819  * Create a new TreePanel. - defaults to fit/scoll contents.
53820  * @param {String/Object} config A string to set only the panel's title, or a config object
53821  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53822  */
53823 Roo.TreePanel = function(config){
53824     var el = config.el;
53825     var tree = config.tree;
53826     delete config.tree; 
53827     delete config.el; // hopefull!
53828     
53829     // wrapper for IE7 strict & safari scroll issue
53830     
53831     var treeEl = el.createChild();
53832     config.resizeEl = treeEl;
53833     
53834     
53835     
53836     Roo.TreePanel.superclass.constructor.call(this, el, config);
53837  
53838  
53839     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53840     //console.log(tree);
53841     this.on('activate', function()
53842     {
53843         if (this.tree.rendered) {
53844             return;
53845         }
53846         //console.log('render tree');
53847         this.tree.render();
53848     });
53849     // this should not be needed.. - it's actually the 'el' that resizes?
53850     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53851     
53852     //this.on('resize',  function (cp, w, h) {
53853     //        this.tree.innerCt.setWidth(w);
53854     //        this.tree.innerCt.setHeight(h);
53855     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53856     //});
53857
53858         
53859     
53860 };
53861
53862 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
53863     fitToFrame : true,
53864     autoScroll : true
53865 });
53866
53867
53868
53869
53870
53871
53872
53873
53874
53875
53876
53877 /*
53878  * Based on:
53879  * Ext JS Library 1.1.1
53880  * Copyright(c) 2006-2007, Ext JS, LLC.
53881  *
53882  * Originally Released Under LGPL - original licence link has changed is not relivant.
53883  *
53884  * Fork - LGPL
53885  * <script type="text/javascript">
53886  */
53887  
53888
53889 /**
53890  * @class Roo.ReaderLayout
53891  * @extends Roo.BorderLayout
53892  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
53893  * center region containing two nested regions (a top one for a list view and one for item preview below),
53894  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
53895  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
53896  * expedites the setup of the overall layout and regions for this common application style.
53897  * Example:
53898  <pre><code>
53899 var reader = new Roo.ReaderLayout();
53900 var CP = Roo.ContentPanel;  // shortcut for adding
53901
53902 reader.beginUpdate();
53903 reader.add("north", new CP("north", "North"));
53904 reader.add("west", new CP("west", {title: "West"}));
53905 reader.add("east", new CP("east", {title: "East"}));
53906
53907 reader.regions.listView.add(new CP("listView", "List"));
53908 reader.regions.preview.add(new CP("preview", "Preview"));
53909 reader.endUpdate();
53910 </code></pre>
53911 * @constructor
53912 * Create a new ReaderLayout
53913 * @param {Object} config Configuration options
53914 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
53915 * document.body if omitted)
53916 */
53917 Roo.ReaderLayout = function(config, renderTo){
53918     var c = config || {size:{}};
53919     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
53920         north: c.north !== false ? Roo.apply({
53921             split:false,
53922             initialSize: 32,
53923             titlebar: false
53924         }, c.north) : false,
53925         west: c.west !== false ? Roo.apply({
53926             split:true,
53927             initialSize: 200,
53928             minSize: 175,
53929             maxSize: 400,
53930             titlebar: true,
53931             collapsible: true,
53932             animate: true,
53933             margins:{left:5,right:0,bottom:5,top:5},
53934             cmargins:{left:5,right:5,bottom:5,top:5}
53935         }, c.west) : false,
53936         east: c.east !== false ? Roo.apply({
53937             split:true,
53938             initialSize: 200,
53939             minSize: 175,
53940             maxSize: 400,
53941             titlebar: true,
53942             collapsible: true,
53943             animate: true,
53944             margins:{left:0,right:5,bottom:5,top:5},
53945             cmargins:{left:5,right:5,bottom:5,top:5}
53946         }, c.east) : false,
53947         center: Roo.apply({
53948             tabPosition: 'top',
53949             autoScroll:false,
53950             closeOnTab: true,
53951             titlebar:false,
53952             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
53953         }, c.center)
53954     });
53955
53956     this.el.addClass('x-reader');
53957
53958     this.beginUpdate();
53959
53960     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
53961         south: c.preview !== false ? Roo.apply({
53962             split:true,
53963             initialSize: 200,
53964             minSize: 100,
53965             autoScroll:true,
53966             collapsible:true,
53967             titlebar: true,
53968             cmargins:{top:5,left:0, right:0, bottom:0}
53969         }, c.preview) : false,
53970         center: Roo.apply({
53971             autoScroll:false,
53972             titlebar:false,
53973             minHeight:200
53974         }, c.listView)
53975     });
53976     this.add('center', new Roo.NestedLayoutPanel(inner,
53977             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
53978
53979     this.endUpdate();
53980
53981     this.regions.preview = inner.getRegion('south');
53982     this.regions.listView = inner.getRegion('center');
53983 };
53984
53985 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
53986  * Based on:
53987  * Ext JS Library 1.1.1
53988  * Copyright(c) 2006-2007, Ext JS, LLC.
53989  *
53990  * Originally Released Under LGPL - original licence link has changed is not relivant.
53991  *
53992  * Fork - LGPL
53993  * <script type="text/javascript">
53994  */
53995  
53996 /**
53997  * @class Roo.grid.Grid
53998  * @extends Roo.util.Observable
53999  * This class represents the primary interface of a component based grid control.
54000  * <br><br>Usage:<pre><code>
54001  var grid = new Roo.grid.Grid("my-container-id", {
54002      ds: myDataStore,
54003      cm: myColModel,
54004      selModel: mySelectionModel,
54005      autoSizeColumns: true,
54006      monitorWindowResize: false,
54007      trackMouseOver: true
54008  });
54009  // set any options
54010  grid.render();
54011  * </code></pre>
54012  * <b>Common Problems:</b><br/>
54013  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54014  * element will correct this<br/>
54015  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54016  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54017  * are unpredictable.<br/>
54018  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54019  * grid to calculate dimensions/offsets.<br/>
54020   * @constructor
54021  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54022  * The container MUST have some type of size defined for the grid to fill. The container will be
54023  * automatically set to position relative if it isn't already.
54024  * @param {Object} config A config object that sets properties on this grid.
54025  */
54026 Roo.grid.Grid = function(container, config){
54027         // initialize the container
54028         this.container = Roo.get(container);
54029         this.container.update("");
54030         this.container.setStyle("overflow", "hidden");
54031     this.container.addClass('x-grid-container');
54032
54033     this.id = this.container.id;
54034
54035     Roo.apply(this, config);
54036     // check and correct shorthanded configs
54037     if(this.ds){
54038         this.dataSource = this.ds;
54039         delete this.ds;
54040     }
54041     if(this.cm){
54042         this.colModel = this.cm;
54043         delete this.cm;
54044     }
54045     if(this.sm){
54046         this.selModel = this.sm;
54047         delete this.sm;
54048     }
54049
54050     if (this.selModel) {
54051         this.selModel = Roo.factory(this.selModel, Roo.grid);
54052         this.sm = this.selModel;
54053         this.sm.xmodule = this.xmodule || false;
54054     }
54055     if (typeof(this.colModel.config) == 'undefined') {
54056         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54057         this.cm = this.colModel;
54058         this.cm.xmodule = this.xmodule || false;
54059     }
54060     if (this.dataSource) {
54061         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54062         this.ds = this.dataSource;
54063         this.ds.xmodule = this.xmodule || false;
54064          
54065     }
54066     
54067     
54068     
54069     if(this.width){
54070         this.container.setWidth(this.width);
54071     }
54072
54073     if(this.height){
54074         this.container.setHeight(this.height);
54075     }
54076     /** @private */
54077         this.addEvents({
54078         // raw events
54079         /**
54080          * @event click
54081          * The raw click event for the entire grid.
54082          * @param {Roo.EventObject} e
54083          */
54084         "click" : true,
54085         /**
54086          * @event dblclick
54087          * The raw dblclick event for the entire grid.
54088          * @param {Roo.EventObject} e
54089          */
54090         "dblclick" : true,
54091         /**
54092          * @event contextmenu
54093          * The raw contextmenu event for the entire grid.
54094          * @param {Roo.EventObject} e
54095          */
54096         "contextmenu" : true,
54097         /**
54098          * @event mousedown
54099          * The raw mousedown event for the entire grid.
54100          * @param {Roo.EventObject} e
54101          */
54102         "mousedown" : true,
54103         /**
54104          * @event mouseup
54105          * The raw mouseup event for the entire grid.
54106          * @param {Roo.EventObject} e
54107          */
54108         "mouseup" : true,
54109         /**
54110          * @event mouseover
54111          * The raw mouseover event for the entire grid.
54112          * @param {Roo.EventObject} e
54113          */
54114         "mouseover" : true,
54115         /**
54116          * @event mouseout
54117          * The raw mouseout event for the entire grid.
54118          * @param {Roo.EventObject} e
54119          */
54120         "mouseout" : true,
54121         /**
54122          * @event keypress
54123          * The raw keypress event for the entire grid.
54124          * @param {Roo.EventObject} e
54125          */
54126         "keypress" : true,
54127         /**
54128          * @event keydown
54129          * The raw keydown event for the entire grid.
54130          * @param {Roo.EventObject} e
54131          */
54132         "keydown" : true,
54133
54134         // custom events
54135
54136         /**
54137          * @event cellclick
54138          * Fires when a cell is clicked
54139          * @param {Grid} this
54140          * @param {Number} rowIndex
54141          * @param {Number} columnIndex
54142          * @param {Roo.EventObject} e
54143          */
54144         "cellclick" : true,
54145         /**
54146          * @event celldblclick
54147          * Fires when a cell is double clicked
54148          * @param {Grid} this
54149          * @param {Number} rowIndex
54150          * @param {Number} columnIndex
54151          * @param {Roo.EventObject} e
54152          */
54153         "celldblclick" : true,
54154         /**
54155          * @event rowclick
54156          * Fires when a row is clicked
54157          * @param {Grid} this
54158          * @param {Number} rowIndex
54159          * @param {Roo.EventObject} e
54160          */
54161         "rowclick" : true,
54162         /**
54163          * @event rowdblclick
54164          * Fires when a row is double clicked
54165          * @param {Grid} this
54166          * @param {Number} rowIndex
54167          * @param {Roo.EventObject} e
54168          */
54169         "rowdblclick" : true,
54170         /**
54171          * @event headerclick
54172          * Fires when a header is clicked
54173          * @param {Grid} this
54174          * @param {Number} columnIndex
54175          * @param {Roo.EventObject} e
54176          */
54177         "headerclick" : true,
54178         /**
54179          * @event headerdblclick
54180          * Fires when a header cell is double clicked
54181          * @param {Grid} this
54182          * @param {Number} columnIndex
54183          * @param {Roo.EventObject} e
54184          */
54185         "headerdblclick" : true,
54186         /**
54187          * @event rowcontextmenu
54188          * Fires when a row is right clicked
54189          * @param {Grid} this
54190          * @param {Number} rowIndex
54191          * @param {Roo.EventObject} e
54192          */
54193         "rowcontextmenu" : true,
54194         /**
54195          * @event cellcontextmenu
54196          * Fires when a cell is right clicked
54197          * @param {Grid} this
54198          * @param {Number} rowIndex
54199          * @param {Number} cellIndex
54200          * @param {Roo.EventObject} e
54201          */
54202          "cellcontextmenu" : true,
54203         /**
54204          * @event headercontextmenu
54205          * Fires when a header is right clicked
54206          * @param {Grid} this
54207          * @param {Number} columnIndex
54208          * @param {Roo.EventObject} e
54209          */
54210         "headercontextmenu" : true,
54211         /**
54212          * @event bodyscroll
54213          * Fires when the body element is scrolled
54214          * @param {Number} scrollLeft
54215          * @param {Number} scrollTop
54216          */
54217         "bodyscroll" : true,
54218         /**
54219          * @event columnresize
54220          * Fires when the user resizes a column
54221          * @param {Number} columnIndex
54222          * @param {Number} newSize
54223          */
54224         "columnresize" : true,
54225         /**
54226          * @event columnmove
54227          * Fires when the user moves a column
54228          * @param {Number} oldIndex
54229          * @param {Number} newIndex
54230          */
54231         "columnmove" : true,
54232         /**
54233          * @event startdrag
54234          * Fires when row(s) start being dragged
54235          * @param {Grid} this
54236          * @param {Roo.GridDD} dd The drag drop object
54237          * @param {event} e The raw browser event
54238          */
54239         "startdrag" : true,
54240         /**
54241          * @event enddrag
54242          * Fires when a drag operation is complete
54243          * @param {Grid} this
54244          * @param {Roo.GridDD} dd The drag drop object
54245          * @param {event} e The raw browser event
54246          */
54247         "enddrag" : true,
54248         /**
54249          * @event dragdrop
54250          * Fires when dragged row(s) are dropped on a valid DD target
54251          * @param {Grid} this
54252          * @param {Roo.GridDD} dd The drag drop object
54253          * @param {String} targetId The target drag drop object
54254          * @param {event} e The raw browser event
54255          */
54256         "dragdrop" : true,
54257         /**
54258          * @event dragover
54259          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54260          * @param {Grid} this
54261          * @param {Roo.GridDD} dd The drag drop object
54262          * @param {String} targetId The target drag drop object
54263          * @param {event} e The raw browser event
54264          */
54265         "dragover" : true,
54266         /**
54267          * @event dragenter
54268          *  Fires when the dragged row(s) first cross another DD target while being dragged
54269          * @param {Grid} this
54270          * @param {Roo.GridDD} dd The drag drop object
54271          * @param {String} targetId The target drag drop object
54272          * @param {event} e The raw browser event
54273          */
54274         "dragenter" : true,
54275         /**
54276          * @event dragout
54277          * Fires when the dragged row(s) leave another DD target while being dragged
54278          * @param {Grid} this
54279          * @param {Roo.GridDD} dd The drag drop object
54280          * @param {String} targetId The target drag drop object
54281          * @param {event} e The raw browser event
54282          */
54283         "dragout" : true,
54284         /**
54285          * @event rowclass
54286          * Fires when a row is rendered, so you can change add a style to it.
54287          * @param {GridView} gridview   The grid view
54288          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54289          */
54290         'rowclass' : true,
54291
54292         /**
54293          * @event render
54294          * Fires when the grid is rendered
54295          * @param {Grid} grid
54296          */
54297         'render' : true
54298     });
54299
54300     Roo.grid.Grid.superclass.constructor.call(this);
54301 };
54302 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54303     
54304     /**
54305      * @cfg {String} ddGroup - drag drop group.
54306      */
54307
54308     /**
54309      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54310      */
54311     minColumnWidth : 25,
54312
54313     /**
54314      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54315      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54316      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54317      */
54318     autoSizeColumns : false,
54319
54320     /**
54321      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54322      */
54323     autoSizeHeaders : true,
54324
54325     /**
54326      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54327      */
54328     monitorWindowResize : true,
54329
54330     /**
54331      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54332      * rows measured to get a columns size. Default is 0 (all rows).
54333      */
54334     maxRowsToMeasure : 0,
54335
54336     /**
54337      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54338      */
54339     trackMouseOver : true,
54340
54341     /**
54342     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54343     */
54344     
54345     /**
54346     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54347     */
54348     enableDragDrop : false,
54349     
54350     /**
54351     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54352     */
54353     enableColumnMove : true,
54354     
54355     /**
54356     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54357     */
54358     enableColumnHide : true,
54359     
54360     /**
54361     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54362     */
54363     enableRowHeightSync : false,
54364     
54365     /**
54366     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54367     */
54368     stripeRows : true,
54369     
54370     /**
54371     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54372     */
54373     autoHeight : false,
54374
54375     /**
54376      * @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.
54377      */
54378     autoExpandColumn : false,
54379
54380     /**
54381     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54382     * Default is 50.
54383     */
54384     autoExpandMin : 50,
54385
54386     /**
54387     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54388     */
54389     autoExpandMax : 1000,
54390
54391     /**
54392     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54393     */
54394     view : null,
54395
54396     /**
54397     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54398     */
54399     loadMask : false,
54400     /**
54401     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54402     */
54403     dropTarget: false,
54404     
54405    
54406     
54407     // private
54408     rendered : false,
54409
54410     /**
54411     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54412     * of a fixed width. Default is false.
54413     */
54414     /**
54415     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54416     */
54417     /**
54418      * Called once after all setup has been completed and the grid is ready to be rendered.
54419      * @return {Roo.grid.Grid} this
54420      */
54421     render : function()
54422     {
54423         var c = this.container;
54424         // try to detect autoHeight/width mode
54425         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54426             this.autoHeight = true;
54427         }
54428         var view = this.getView();
54429         view.init(this);
54430
54431         c.on("click", this.onClick, this);
54432         c.on("dblclick", this.onDblClick, this);
54433         c.on("contextmenu", this.onContextMenu, this);
54434         c.on("keydown", this.onKeyDown, this);
54435         if (Roo.isTouch) {
54436             c.on("touchstart", this.onTouchStart, this);
54437         }
54438
54439         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54440
54441         this.getSelectionModel().init(this);
54442
54443         view.render();
54444
54445         if(this.loadMask){
54446             this.loadMask = new Roo.LoadMask(this.container,
54447                     Roo.apply({store:this.dataSource}, this.loadMask));
54448         }
54449         
54450         
54451         if (this.toolbar && this.toolbar.xtype) {
54452             this.toolbar.container = this.getView().getHeaderPanel(true);
54453             this.toolbar = new Roo.Toolbar(this.toolbar);
54454         }
54455         if (this.footer && this.footer.xtype) {
54456             this.footer.dataSource = this.getDataSource();
54457             this.footer.container = this.getView().getFooterPanel(true);
54458             this.footer = Roo.factory(this.footer, Roo);
54459         }
54460         if (this.dropTarget && this.dropTarget.xtype) {
54461             delete this.dropTarget.xtype;
54462             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54463         }
54464         
54465         
54466         this.rendered = true;
54467         this.fireEvent('render', this);
54468         return this;
54469     },
54470
54471         /**
54472          * Reconfigures the grid to use a different Store and Column Model.
54473          * The View will be bound to the new objects and refreshed.
54474          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54475          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54476          */
54477     reconfigure : function(dataSource, colModel){
54478         if(this.loadMask){
54479             this.loadMask.destroy();
54480             this.loadMask = new Roo.LoadMask(this.container,
54481                     Roo.apply({store:dataSource}, this.loadMask));
54482         }
54483         this.view.bind(dataSource, colModel);
54484         this.dataSource = dataSource;
54485         this.colModel = colModel;
54486         this.view.refresh(true);
54487     },
54488
54489     // private
54490     onKeyDown : function(e){
54491         this.fireEvent("keydown", e);
54492     },
54493
54494     /**
54495      * Destroy this grid.
54496      * @param {Boolean} removeEl True to remove the element
54497      */
54498     destroy : function(removeEl, keepListeners){
54499         if(this.loadMask){
54500             this.loadMask.destroy();
54501         }
54502         var c = this.container;
54503         c.removeAllListeners();
54504         this.view.destroy();
54505         this.colModel.purgeListeners();
54506         if(!keepListeners){
54507             this.purgeListeners();
54508         }
54509         c.update("");
54510         if(removeEl === true){
54511             c.remove();
54512         }
54513     },
54514
54515     // private
54516     processEvent : function(name, e){
54517         // does this fire select???
54518         //Roo.log('grid:processEvent '  + name);
54519         
54520         if (name != 'touchstart' ) {
54521             this.fireEvent(name, e);    
54522         }
54523         
54524         var t = e.getTarget();
54525         var v = this.view;
54526         var header = v.findHeaderIndex(t);
54527         if(header !== false){
54528             var ename = name == 'touchstart' ? 'click' : name;
54529              
54530             this.fireEvent("header" + ename, this, header, e);
54531         }else{
54532             var row = v.findRowIndex(t);
54533             var cell = v.findCellIndex(t);
54534             if (name == 'touchstart') {
54535                 // first touch is always a click.
54536                 // hopefull this happens after selection is updated.?
54537                 name = false;
54538                 
54539                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54540                     var cs = this.selModel.getSelectedCell();
54541                     if (row == cs[0] && cell == cs[1]){
54542                         name = 'dblclick';
54543                     }
54544                 }
54545                 if (typeof(this.selModel.getSelections) != 'undefined') {
54546                     var cs = this.selModel.getSelections();
54547                     var ds = this.dataSource;
54548                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54549                         name = 'dblclick';
54550                     }
54551                 }
54552                 if (!name) {
54553                     return;
54554                 }
54555             }
54556             
54557             
54558             if(row !== false){
54559                 this.fireEvent("row" + name, this, row, e);
54560                 if(cell !== false){
54561                     this.fireEvent("cell" + name, this, row, cell, e);
54562                 }
54563             }
54564         }
54565     },
54566
54567     // private
54568     onClick : function(e){
54569         this.processEvent("click", e);
54570     },
54571    // private
54572     onTouchStart : function(e){
54573         this.processEvent("touchstart", e);
54574     },
54575
54576     // private
54577     onContextMenu : function(e, t){
54578         this.processEvent("contextmenu", e);
54579     },
54580
54581     // private
54582     onDblClick : function(e){
54583         this.processEvent("dblclick", e);
54584     },
54585
54586     // private
54587     walkCells : function(row, col, step, fn, scope){
54588         var cm = this.colModel, clen = cm.getColumnCount();
54589         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54590         if(step < 0){
54591             if(col < 0){
54592                 row--;
54593                 first = false;
54594             }
54595             while(row >= 0){
54596                 if(!first){
54597                     col = clen-1;
54598                 }
54599                 first = false;
54600                 while(col >= 0){
54601                     if(fn.call(scope || this, row, col, cm) === true){
54602                         return [row, col];
54603                     }
54604                     col--;
54605                 }
54606                 row--;
54607             }
54608         } else {
54609             if(col >= clen){
54610                 row++;
54611                 first = false;
54612             }
54613             while(row < rlen){
54614                 if(!first){
54615                     col = 0;
54616                 }
54617                 first = false;
54618                 while(col < clen){
54619                     if(fn.call(scope || this, row, col, cm) === true){
54620                         return [row, col];
54621                     }
54622                     col++;
54623                 }
54624                 row++;
54625             }
54626         }
54627         return null;
54628     },
54629
54630     // private
54631     getSelections : function(){
54632         return this.selModel.getSelections();
54633     },
54634
54635     /**
54636      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54637      * but if manual update is required this method will initiate it.
54638      */
54639     autoSize : function(){
54640         if(this.rendered){
54641             this.view.layout();
54642             if(this.view.adjustForScroll){
54643                 this.view.adjustForScroll();
54644             }
54645         }
54646     },
54647
54648     /**
54649      * Returns the grid's underlying element.
54650      * @return {Element} The element
54651      */
54652     getGridEl : function(){
54653         return this.container;
54654     },
54655
54656     // private for compatibility, overridden by editor grid
54657     stopEditing : function(){},
54658
54659     /**
54660      * Returns the grid's SelectionModel.
54661      * @return {SelectionModel}
54662      */
54663     getSelectionModel : function(){
54664         if(!this.selModel){
54665             this.selModel = new Roo.grid.RowSelectionModel();
54666         }
54667         return this.selModel;
54668     },
54669
54670     /**
54671      * Returns the grid's DataSource.
54672      * @return {DataSource}
54673      */
54674     getDataSource : function(){
54675         return this.dataSource;
54676     },
54677
54678     /**
54679      * Returns the grid's ColumnModel.
54680      * @return {ColumnModel}
54681      */
54682     getColumnModel : function(){
54683         return this.colModel;
54684     },
54685
54686     /**
54687      * Returns the grid's GridView object.
54688      * @return {GridView}
54689      */
54690     getView : function(){
54691         if(!this.view){
54692             this.view = new Roo.grid.GridView(this.viewConfig);
54693         }
54694         return this.view;
54695     },
54696     /**
54697      * Called to get grid's drag proxy text, by default returns this.ddText.
54698      * @return {String}
54699      */
54700     getDragDropText : function(){
54701         var count = this.selModel.getCount();
54702         return String.format(this.ddText, count, count == 1 ? '' : 's');
54703     }
54704 });
54705 /**
54706  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54707  * %0 is replaced with the number of selected rows.
54708  * @type String
54709  */
54710 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54711  * Based on:
54712  * Ext JS Library 1.1.1
54713  * Copyright(c) 2006-2007, Ext JS, LLC.
54714  *
54715  * Originally Released Under LGPL - original licence link has changed is not relivant.
54716  *
54717  * Fork - LGPL
54718  * <script type="text/javascript">
54719  */
54720  
54721 Roo.grid.AbstractGridView = function(){
54722         this.grid = null;
54723         
54724         this.events = {
54725             "beforerowremoved" : true,
54726             "beforerowsinserted" : true,
54727             "beforerefresh" : true,
54728             "rowremoved" : true,
54729             "rowsinserted" : true,
54730             "rowupdated" : true,
54731             "refresh" : true
54732         };
54733     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54734 };
54735
54736 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54737     rowClass : "x-grid-row",
54738     cellClass : "x-grid-cell",
54739     tdClass : "x-grid-td",
54740     hdClass : "x-grid-hd",
54741     splitClass : "x-grid-hd-split",
54742     
54743     init: function(grid){
54744         this.grid = grid;
54745                 var cid = this.grid.getGridEl().id;
54746         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54747         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54748         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54749         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54750         },
54751         
54752     getColumnRenderers : function(){
54753         var renderers = [];
54754         var cm = this.grid.colModel;
54755         var colCount = cm.getColumnCount();
54756         for(var i = 0; i < colCount; i++){
54757             renderers[i] = cm.getRenderer(i);
54758         }
54759         return renderers;
54760     },
54761     
54762     getColumnIds : function(){
54763         var ids = [];
54764         var cm = this.grid.colModel;
54765         var colCount = cm.getColumnCount();
54766         for(var i = 0; i < colCount; i++){
54767             ids[i] = cm.getColumnId(i);
54768         }
54769         return ids;
54770     },
54771     
54772     getDataIndexes : function(){
54773         if(!this.indexMap){
54774             this.indexMap = this.buildIndexMap();
54775         }
54776         return this.indexMap.colToData;
54777     },
54778     
54779     getColumnIndexByDataIndex : function(dataIndex){
54780         if(!this.indexMap){
54781             this.indexMap = this.buildIndexMap();
54782         }
54783         return this.indexMap.dataToCol[dataIndex];
54784     },
54785     
54786     /**
54787      * Set a css style for a column dynamically. 
54788      * @param {Number} colIndex The index of the column
54789      * @param {String} name The css property name
54790      * @param {String} value The css value
54791      */
54792     setCSSStyle : function(colIndex, name, value){
54793         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54794         Roo.util.CSS.updateRule(selector, name, value);
54795     },
54796     
54797     generateRules : function(cm){
54798         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54799         Roo.util.CSS.removeStyleSheet(rulesId);
54800         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54801             var cid = cm.getColumnId(i);
54802             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54803                          this.tdSelector, cid, " {\n}\n",
54804                          this.hdSelector, cid, " {\n}\n",
54805                          this.splitSelector, cid, " {\n}\n");
54806         }
54807         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54808     }
54809 });/*
54810  * Based on:
54811  * Ext JS Library 1.1.1
54812  * Copyright(c) 2006-2007, Ext JS, LLC.
54813  *
54814  * Originally Released Under LGPL - original licence link has changed is not relivant.
54815  *
54816  * Fork - LGPL
54817  * <script type="text/javascript">
54818  */
54819
54820 // private
54821 // This is a support class used internally by the Grid components
54822 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54823     this.grid = grid;
54824     this.view = grid.getView();
54825     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54826     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54827     if(hd2){
54828         this.setHandleElId(Roo.id(hd));
54829         this.setOuterHandleElId(Roo.id(hd2));
54830     }
54831     this.scroll = false;
54832 };
54833 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54834     maxDragWidth: 120,
54835     getDragData : function(e){
54836         var t = Roo.lib.Event.getTarget(e);
54837         var h = this.view.findHeaderCell(t);
54838         if(h){
54839             return {ddel: h.firstChild, header:h};
54840         }
54841         return false;
54842     },
54843
54844     onInitDrag : function(e){
54845         this.view.headersDisabled = true;
54846         var clone = this.dragData.ddel.cloneNode(true);
54847         clone.id = Roo.id();
54848         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54849         this.proxy.update(clone);
54850         return true;
54851     },
54852
54853     afterValidDrop : function(){
54854         var v = this.view;
54855         setTimeout(function(){
54856             v.headersDisabled = false;
54857         }, 50);
54858     },
54859
54860     afterInvalidDrop : function(){
54861         var v = this.view;
54862         setTimeout(function(){
54863             v.headersDisabled = false;
54864         }, 50);
54865     }
54866 });
54867 /*
54868  * Based on:
54869  * Ext JS Library 1.1.1
54870  * Copyright(c) 2006-2007, Ext JS, LLC.
54871  *
54872  * Originally Released Under LGPL - original licence link has changed is not relivant.
54873  *
54874  * Fork - LGPL
54875  * <script type="text/javascript">
54876  */
54877 // private
54878 // This is a support class used internally by the Grid components
54879 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
54880     this.grid = grid;
54881     this.view = grid.getView();
54882     // split the proxies so they don't interfere with mouse events
54883     this.proxyTop = Roo.DomHelper.append(document.body, {
54884         cls:"col-move-top", html:"&#160;"
54885     }, true);
54886     this.proxyBottom = Roo.DomHelper.append(document.body, {
54887         cls:"col-move-bottom", html:"&#160;"
54888     }, true);
54889     this.proxyTop.hide = this.proxyBottom.hide = function(){
54890         this.setLeftTop(-100,-100);
54891         this.setStyle("visibility", "hidden");
54892     };
54893     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54894     // temporarily disabled
54895     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
54896     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
54897 };
54898 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
54899     proxyOffsets : [-4, -9],
54900     fly: Roo.Element.fly,
54901
54902     getTargetFromEvent : function(e){
54903         var t = Roo.lib.Event.getTarget(e);
54904         var cindex = this.view.findCellIndex(t);
54905         if(cindex !== false){
54906             return this.view.getHeaderCell(cindex);
54907         }
54908         return null;
54909     },
54910
54911     nextVisible : function(h){
54912         var v = this.view, cm = this.grid.colModel;
54913         h = h.nextSibling;
54914         while(h){
54915             if(!cm.isHidden(v.getCellIndex(h))){
54916                 return h;
54917             }
54918             h = h.nextSibling;
54919         }
54920         return null;
54921     },
54922
54923     prevVisible : function(h){
54924         var v = this.view, cm = this.grid.colModel;
54925         h = h.prevSibling;
54926         while(h){
54927             if(!cm.isHidden(v.getCellIndex(h))){
54928                 return h;
54929             }
54930             h = h.prevSibling;
54931         }
54932         return null;
54933     },
54934
54935     positionIndicator : function(h, n, e){
54936         var x = Roo.lib.Event.getPageX(e);
54937         var r = Roo.lib.Dom.getRegion(n.firstChild);
54938         var px, pt, py = r.top + this.proxyOffsets[1];
54939         if((r.right - x) <= (r.right-r.left)/2){
54940             px = r.right+this.view.borderWidth;
54941             pt = "after";
54942         }else{
54943             px = r.left;
54944             pt = "before";
54945         }
54946         var oldIndex = this.view.getCellIndex(h);
54947         var newIndex = this.view.getCellIndex(n);
54948
54949         if(this.grid.colModel.isFixed(newIndex)){
54950             return false;
54951         }
54952
54953         var locked = this.grid.colModel.isLocked(newIndex);
54954
54955         if(pt == "after"){
54956             newIndex++;
54957         }
54958         if(oldIndex < newIndex){
54959             newIndex--;
54960         }
54961         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
54962             return false;
54963         }
54964         px +=  this.proxyOffsets[0];
54965         this.proxyTop.setLeftTop(px, py);
54966         this.proxyTop.show();
54967         if(!this.bottomOffset){
54968             this.bottomOffset = this.view.mainHd.getHeight();
54969         }
54970         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
54971         this.proxyBottom.show();
54972         return pt;
54973     },
54974
54975     onNodeEnter : function(n, dd, e, data){
54976         if(data.header != n){
54977             this.positionIndicator(data.header, n, e);
54978         }
54979     },
54980
54981     onNodeOver : function(n, dd, e, data){
54982         var result = false;
54983         if(data.header != n){
54984             result = this.positionIndicator(data.header, n, e);
54985         }
54986         if(!result){
54987             this.proxyTop.hide();
54988             this.proxyBottom.hide();
54989         }
54990         return result ? this.dropAllowed : this.dropNotAllowed;
54991     },
54992
54993     onNodeOut : function(n, dd, e, data){
54994         this.proxyTop.hide();
54995         this.proxyBottom.hide();
54996     },
54997
54998     onNodeDrop : function(n, dd, e, data){
54999         var h = data.header;
55000         if(h != n){
55001             var cm = this.grid.colModel;
55002             var x = Roo.lib.Event.getPageX(e);
55003             var r = Roo.lib.Dom.getRegion(n.firstChild);
55004             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
55005             var oldIndex = this.view.getCellIndex(h);
55006             var newIndex = this.view.getCellIndex(n);
55007             var locked = cm.isLocked(newIndex);
55008             if(pt == "after"){
55009                 newIndex++;
55010             }
55011             if(oldIndex < newIndex){
55012                 newIndex--;
55013             }
55014             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55015                 return false;
55016             }
55017             cm.setLocked(oldIndex, locked, true);
55018             cm.moveColumn(oldIndex, newIndex);
55019             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55020             return true;
55021         }
55022         return false;
55023     }
55024 });
55025 /*
55026  * Based on:
55027  * Ext JS Library 1.1.1
55028  * Copyright(c) 2006-2007, Ext JS, LLC.
55029  *
55030  * Originally Released Under LGPL - original licence link has changed is not relivant.
55031  *
55032  * Fork - LGPL
55033  * <script type="text/javascript">
55034  */
55035   
55036 /**
55037  * @class Roo.grid.GridView
55038  * @extends Roo.util.Observable
55039  *
55040  * @constructor
55041  * @param {Object} config
55042  */
55043 Roo.grid.GridView = function(config){
55044     Roo.grid.GridView.superclass.constructor.call(this);
55045     this.el = null;
55046
55047     Roo.apply(this, config);
55048 };
55049
55050 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55051
55052     unselectable :  'unselectable="on"',
55053     unselectableCls :  'x-unselectable',
55054     
55055     
55056     rowClass : "x-grid-row",
55057
55058     cellClass : "x-grid-col",
55059
55060     tdClass : "x-grid-td",
55061
55062     hdClass : "x-grid-hd",
55063
55064     splitClass : "x-grid-split",
55065
55066     sortClasses : ["sort-asc", "sort-desc"],
55067
55068     enableMoveAnim : false,
55069
55070     hlColor: "C3DAF9",
55071
55072     dh : Roo.DomHelper,
55073
55074     fly : Roo.Element.fly,
55075
55076     css : Roo.util.CSS,
55077
55078     borderWidth: 1,
55079
55080     splitOffset: 3,
55081
55082     scrollIncrement : 22,
55083
55084     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55085
55086     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55087
55088     bind : function(ds, cm){
55089         if(this.ds){
55090             this.ds.un("load", this.onLoad, this);
55091             this.ds.un("datachanged", this.onDataChange, this);
55092             this.ds.un("add", this.onAdd, this);
55093             this.ds.un("remove", this.onRemove, this);
55094             this.ds.un("update", this.onUpdate, this);
55095             this.ds.un("clear", this.onClear, this);
55096         }
55097         if(ds){
55098             ds.on("load", this.onLoad, this);
55099             ds.on("datachanged", this.onDataChange, this);
55100             ds.on("add", this.onAdd, this);
55101             ds.on("remove", this.onRemove, this);
55102             ds.on("update", this.onUpdate, this);
55103             ds.on("clear", this.onClear, this);
55104         }
55105         this.ds = ds;
55106
55107         if(this.cm){
55108             this.cm.un("widthchange", this.onColWidthChange, this);
55109             this.cm.un("headerchange", this.onHeaderChange, this);
55110             this.cm.un("hiddenchange", this.onHiddenChange, this);
55111             this.cm.un("columnmoved", this.onColumnMove, this);
55112             this.cm.un("columnlockchange", this.onColumnLock, this);
55113         }
55114         if(cm){
55115             this.generateRules(cm);
55116             cm.on("widthchange", this.onColWidthChange, this);
55117             cm.on("headerchange", this.onHeaderChange, this);
55118             cm.on("hiddenchange", this.onHiddenChange, this);
55119             cm.on("columnmoved", this.onColumnMove, this);
55120             cm.on("columnlockchange", this.onColumnLock, this);
55121         }
55122         this.cm = cm;
55123     },
55124
55125     init: function(grid){
55126         Roo.grid.GridView.superclass.init.call(this, grid);
55127
55128         this.bind(grid.dataSource, grid.colModel);
55129
55130         grid.on("headerclick", this.handleHeaderClick, this);
55131
55132         if(grid.trackMouseOver){
55133             grid.on("mouseover", this.onRowOver, this);
55134             grid.on("mouseout", this.onRowOut, this);
55135         }
55136         grid.cancelTextSelection = function(){};
55137         this.gridId = grid.id;
55138
55139         var tpls = this.templates || {};
55140
55141         if(!tpls.master){
55142             tpls.master = new Roo.Template(
55143                '<div class="x-grid" hidefocus="true">',
55144                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55145                   '<div class="x-grid-topbar"></div>',
55146                   '<div class="x-grid-scroller"><div></div></div>',
55147                   '<div class="x-grid-locked">',
55148                       '<div class="x-grid-header">{lockedHeader}</div>',
55149                       '<div class="x-grid-body">{lockedBody}</div>',
55150                   "</div>",
55151                   '<div class="x-grid-viewport">',
55152                       '<div class="x-grid-header">{header}</div>',
55153                       '<div class="x-grid-body">{body}</div>',
55154                   "</div>",
55155                   '<div class="x-grid-bottombar"></div>',
55156                  
55157                   '<div class="x-grid-resize-proxy">&#160;</div>',
55158                "</div>"
55159             );
55160             tpls.master.disableformats = true;
55161         }
55162
55163         if(!tpls.header){
55164             tpls.header = new Roo.Template(
55165                '<table border="0" cellspacing="0" cellpadding="0">',
55166                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55167                "</table>{splits}"
55168             );
55169             tpls.header.disableformats = true;
55170         }
55171         tpls.header.compile();
55172
55173         if(!tpls.hcell){
55174             tpls.hcell = new Roo.Template(
55175                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55176                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55177                 "</div></td>"
55178              );
55179              tpls.hcell.disableFormats = true;
55180         }
55181         tpls.hcell.compile();
55182
55183         if(!tpls.hsplit){
55184             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55185                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55186             tpls.hsplit.disableFormats = true;
55187         }
55188         tpls.hsplit.compile();
55189
55190         if(!tpls.body){
55191             tpls.body = new Roo.Template(
55192                '<table border="0" cellspacing="0" cellpadding="0">',
55193                "<tbody>{rows}</tbody>",
55194                "</table>"
55195             );
55196             tpls.body.disableFormats = true;
55197         }
55198         tpls.body.compile();
55199
55200         if(!tpls.row){
55201             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55202             tpls.row.disableFormats = true;
55203         }
55204         tpls.row.compile();
55205
55206         if(!tpls.cell){
55207             tpls.cell = new Roo.Template(
55208                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55209                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55210                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55211                 "</td>"
55212             );
55213             tpls.cell.disableFormats = true;
55214         }
55215         tpls.cell.compile();
55216
55217         this.templates = tpls;
55218     },
55219
55220     // remap these for backwards compat
55221     onColWidthChange : function(){
55222         this.updateColumns.apply(this, arguments);
55223     },
55224     onHeaderChange : function(){
55225         this.updateHeaders.apply(this, arguments);
55226     }, 
55227     onHiddenChange : function(){
55228         this.handleHiddenChange.apply(this, arguments);
55229     },
55230     onColumnMove : function(){
55231         this.handleColumnMove.apply(this, arguments);
55232     },
55233     onColumnLock : function(){
55234         this.handleLockChange.apply(this, arguments);
55235     },
55236
55237     onDataChange : function(){
55238         this.refresh();
55239         this.updateHeaderSortState();
55240     },
55241
55242     onClear : function(){
55243         this.refresh();
55244     },
55245
55246     onUpdate : function(ds, record){
55247         this.refreshRow(record);
55248     },
55249
55250     refreshRow : function(record){
55251         var ds = this.ds, index;
55252         if(typeof record == 'number'){
55253             index = record;
55254             record = ds.getAt(index);
55255         }else{
55256             index = ds.indexOf(record);
55257         }
55258         this.insertRows(ds, index, index, true);
55259         this.onRemove(ds, record, index+1, true);
55260         this.syncRowHeights(index, index);
55261         this.layout();
55262         this.fireEvent("rowupdated", this, index, record);
55263     },
55264
55265     onAdd : function(ds, records, index){
55266         this.insertRows(ds, index, index + (records.length-1));
55267     },
55268
55269     onRemove : function(ds, record, index, isUpdate){
55270         if(isUpdate !== true){
55271             this.fireEvent("beforerowremoved", this, index, record);
55272         }
55273         var bt = this.getBodyTable(), lt = this.getLockedTable();
55274         if(bt.rows[index]){
55275             bt.firstChild.removeChild(bt.rows[index]);
55276         }
55277         if(lt.rows[index]){
55278             lt.firstChild.removeChild(lt.rows[index]);
55279         }
55280         if(isUpdate !== true){
55281             this.stripeRows(index);
55282             this.syncRowHeights(index, index);
55283             this.layout();
55284             this.fireEvent("rowremoved", this, index, record);
55285         }
55286     },
55287
55288     onLoad : function(){
55289         this.scrollToTop();
55290     },
55291
55292     /**
55293      * Scrolls the grid to the top
55294      */
55295     scrollToTop : function(){
55296         if(this.scroller){
55297             this.scroller.dom.scrollTop = 0;
55298             this.syncScroll();
55299         }
55300     },
55301
55302     /**
55303      * Gets a panel in the header of the grid that can be used for toolbars etc.
55304      * After modifying the contents of this panel a call to grid.autoSize() may be
55305      * required to register any changes in size.
55306      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55307      * @return Roo.Element
55308      */
55309     getHeaderPanel : function(doShow){
55310         if(doShow){
55311             this.headerPanel.show();
55312         }
55313         return this.headerPanel;
55314     },
55315
55316     /**
55317      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55318      * After modifying the contents of this panel a call to grid.autoSize() may be
55319      * required to register any changes in size.
55320      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55321      * @return Roo.Element
55322      */
55323     getFooterPanel : function(doShow){
55324         if(doShow){
55325             this.footerPanel.show();
55326         }
55327         return this.footerPanel;
55328     },
55329
55330     initElements : function(){
55331         var E = Roo.Element;
55332         var el = this.grid.getGridEl().dom.firstChild;
55333         var cs = el.childNodes;
55334
55335         this.el = new E(el);
55336         
55337          this.focusEl = new E(el.firstChild);
55338         this.focusEl.swallowEvent("click", true);
55339         
55340         this.headerPanel = new E(cs[1]);
55341         this.headerPanel.enableDisplayMode("block");
55342
55343         this.scroller = new E(cs[2]);
55344         this.scrollSizer = new E(this.scroller.dom.firstChild);
55345
55346         this.lockedWrap = new E(cs[3]);
55347         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55348         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55349
55350         this.mainWrap = new E(cs[4]);
55351         this.mainHd = new E(this.mainWrap.dom.firstChild);
55352         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55353
55354         this.footerPanel = new E(cs[5]);
55355         this.footerPanel.enableDisplayMode("block");
55356
55357         this.resizeProxy = new E(cs[6]);
55358
55359         this.headerSelector = String.format(
55360            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55361            this.lockedHd.id, this.mainHd.id
55362         );
55363
55364         this.splitterSelector = String.format(
55365            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55366            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55367         );
55368     },
55369     idToCssName : function(s)
55370     {
55371         return s.replace(/[^a-z0-9]+/ig, '-');
55372     },
55373
55374     getHeaderCell : function(index){
55375         return Roo.DomQuery.select(this.headerSelector)[index];
55376     },
55377
55378     getHeaderCellMeasure : function(index){
55379         return this.getHeaderCell(index).firstChild;
55380     },
55381
55382     getHeaderCellText : function(index){
55383         return this.getHeaderCell(index).firstChild.firstChild;
55384     },
55385
55386     getLockedTable : function(){
55387         return this.lockedBody.dom.firstChild;
55388     },
55389
55390     getBodyTable : function(){
55391         return this.mainBody.dom.firstChild;
55392     },
55393
55394     getLockedRow : function(index){
55395         return this.getLockedTable().rows[index];
55396     },
55397
55398     getRow : function(index){
55399         return this.getBodyTable().rows[index];
55400     },
55401
55402     getRowComposite : function(index){
55403         if(!this.rowEl){
55404             this.rowEl = new Roo.CompositeElementLite();
55405         }
55406         var els = [], lrow, mrow;
55407         if(lrow = this.getLockedRow(index)){
55408             els.push(lrow);
55409         }
55410         if(mrow = this.getRow(index)){
55411             els.push(mrow);
55412         }
55413         this.rowEl.elements = els;
55414         return this.rowEl;
55415     },
55416     /**
55417      * Gets the 'td' of the cell
55418      * 
55419      * @param {Integer} rowIndex row to select
55420      * @param {Integer} colIndex column to select
55421      * 
55422      * @return {Object} 
55423      */
55424     getCell : function(rowIndex, colIndex){
55425         var locked = this.cm.getLockedCount();
55426         var source;
55427         if(colIndex < locked){
55428             source = this.lockedBody.dom.firstChild;
55429         }else{
55430             source = this.mainBody.dom.firstChild;
55431             colIndex -= locked;
55432         }
55433         return source.rows[rowIndex].childNodes[colIndex];
55434     },
55435
55436     getCellText : function(rowIndex, colIndex){
55437         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55438     },
55439
55440     getCellBox : function(cell){
55441         var b = this.fly(cell).getBox();
55442         if(Roo.isOpera){ // opera fails to report the Y
55443             b.y = cell.offsetTop + this.mainBody.getY();
55444         }
55445         return b;
55446     },
55447
55448     getCellIndex : function(cell){
55449         var id = String(cell.className).match(this.cellRE);
55450         if(id){
55451             return parseInt(id[1], 10);
55452         }
55453         return 0;
55454     },
55455
55456     findHeaderIndex : function(n){
55457         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55458         return r ? this.getCellIndex(r) : false;
55459     },
55460
55461     findHeaderCell : function(n){
55462         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55463         return r ? r : false;
55464     },
55465
55466     findRowIndex : function(n){
55467         if(!n){
55468             return false;
55469         }
55470         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55471         return r ? r.rowIndex : false;
55472     },
55473
55474     findCellIndex : function(node){
55475         var stop = this.el.dom;
55476         while(node && node != stop){
55477             if(this.findRE.test(node.className)){
55478                 return this.getCellIndex(node);
55479             }
55480             node = node.parentNode;
55481         }
55482         return false;
55483     },
55484
55485     getColumnId : function(index){
55486         return this.cm.getColumnId(index);
55487     },
55488
55489     getSplitters : function()
55490     {
55491         if(this.splitterSelector){
55492            return Roo.DomQuery.select(this.splitterSelector);
55493         }else{
55494             return null;
55495       }
55496     },
55497
55498     getSplitter : function(index){
55499         return this.getSplitters()[index];
55500     },
55501
55502     onRowOver : function(e, t){
55503         var row;
55504         if((row = this.findRowIndex(t)) !== false){
55505             this.getRowComposite(row).addClass("x-grid-row-over");
55506         }
55507     },
55508
55509     onRowOut : function(e, t){
55510         var row;
55511         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55512             this.getRowComposite(row).removeClass("x-grid-row-over");
55513         }
55514     },
55515
55516     renderHeaders : function(){
55517         var cm = this.cm;
55518         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55519         var cb = [], lb = [], sb = [], lsb = [], p = {};
55520         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55521             p.cellId = "x-grid-hd-0-" + i;
55522             p.splitId = "x-grid-csplit-0-" + i;
55523             p.id = cm.getColumnId(i);
55524             p.value = cm.getColumnHeader(i) || "";
55525             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55526             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55527             if(!cm.isLocked(i)){
55528                 cb[cb.length] = ct.apply(p);
55529                 sb[sb.length] = st.apply(p);
55530             }else{
55531                 lb[lb.length] = ct.apply(p);
55532                 lsb[lsb.length] = st.apply(p);
55533             }
55534         }
55535         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55536                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55537     },
55538
55539     updateHeaders : function(){
55540         var html = this.renderHeaders();
55541         this.lockedHd.update(html[0]);
55542         this.mainHd.update(html[1]);
55543     },
55544
55545     /**
55546      * Focuses the specified row.
55547      * @param {Number} row The row index
55548      */
55549     focusRow : function(row)
55550     {
55551         //Roo.log('GridView.focusRow');
55552         var x = this.scroller.dom.scrollLeft;
55553         this.focusCell(row, 0, false);
55554         this.scroller.dom.scrollLeft = x;
55555     },
55556
55557     /**
55558      * Focuses the specified cell.
55559      * @param {Number} row The row index
55560      * @param {Number} col The column index
55561      * @param {Boolean} hscroll false to disable horizontal scrolling
55562      */
55563     focusCell : function(row, col, hscroll)
55564     {
55565         //Roo.log('GridView.focusCell');
55566         var el = this.ensureVisible(row, col, hscroll);
55567         this.focusEl.alignTo(el, "tl-tl");
55568         if(Roo.isGecko){
55569             this.focusEl.focus();
55570         }else{
55571             this.focusEl.focus.defer(1, this.focusEl);
55572         }
55573     },
55574
55575     /**
55576      * Scrolls the specified cell into view
55577      * @param {Number} row The row index
55578      * @param {Number} col The column index
55579      * @param {Boolean} hscroll false to disable horizontal scrolling
55580      */
55581     ensureVisible : function(row, col, hscroll)
55582     {
55583         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55584         //return null; //disable for testing.
55585         if(typeof row != "number"){
55586             row = row.rowIndex;
55587         }
55588         if(row < 0 && row >= this.ds.getCount()){
55589             return  null;
55590         }
55591         col = (col !== undefined ? col : 0);
55592         var cm = this.grid.colModel;
55593         while(cm.isHidden(col)){
55594             col++;
55595         }
55596
55597         var el = this.getCell(row, col);
55598         if(!el){
55599             return null;
55600         }
55601         var c = this.scroller.dom;
55602
55603         var ctop = parseInt(el.offsetTop, 10);
55604         var cleft = parseInt(el.offsetLeft, 10);
55605         var cbot = ctop + el.offsetHeight;
55606         var cright = cleft + el.offsetWidth;
55607         
55608         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55609         var stop = parseInt(c.scrollTop, 10);
55610         var sleft = parseInt(c.scrollLeft, 10);
55611         var sbot = stop + ch;
55612         var sright = sleft + c.clientWidth;
55613         /*
55614         Roo.log('GridView.ensureVisible:' +
55615                 ' ctop:' + ctop +
55616                 ' c.clientHeight:' + c.clientHeight +
55617                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55618                 ' stop:' + stop +
55619                 ' cbot:' + cbot +
55620                 ' sbot:' + sbot +
55621                 ' ch:' + ch  
55622                 );
55623         */
55624         if(ctop < stop){
55625              c.scrollTop = ctop;
55626             //Roo.log("set scrolltop to ctop DISABLE?");
55627         }else if(cbot > sbot){
55628             //Roo.log("set scrolltop to cbot-ch");
55629             c.scrollTop = cbot-ch;
55630         }
55631         
55632         if(hscroll !== false){
55633             if(cleft < sleft){
55634                 c.scrollLeft = cleft;
55635             }else if(cright > sright){
55636                 c.scrollLeft = cright-c.clientWidth;
55637             }
55638         }
55639          
55640         return el;
55641     },
55642
55643     updateColumns : function(){
55644         this.grid.stopEditing();
55645         var cm = this.grid.colModel, colIds = this.getColumnIds();
55646         //var totalWidth = cm.getTotalWidth();
55647         var pos = 0;
55648         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55649             //if(cm.isHidden(i)) continue;
55650             var w = cm.getColumnWidth(i);
55651             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55652             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55653         }
55654         this.updateSplitters();
55655     },
55656
55657     generateRules : function(cm){
55658         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55659         Roo.util.CSS.removeStyleSheet(rulesId);
55660         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55661             var cid = cm.getColumnId(i);
55662             var align = '';
55663             if(cm.config[i].align){
55664                 align = 'text-align:'+cm.config[i].align+';';
55665             }
55666             var hidden = '';
55667             if(cm.isHidden(i)){
55668                 hidden = 'display:none;';
55669             }
55670             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55671             ruleBuf.push(
55672                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55673                     this.hdSelector, cid, " {\n", align, width, "}\n",
55674                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55675                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55676         }
55677         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55678     },
55679
55680     updateSplitters : function(){
55681         var cm = this.cm, s = this.getSplitters();
55682         if(s){ // splitters not created yet
55683             var pos = 0, locked = true;
55684             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55685                 if(cm.isHidden(i)) {
55686                     continue;
55687                 }
55688                 var w = cm.getColumnWidth(i); // make sure it's a number
55689                 if(!cm.isLocked(i) && locked){
55690                     pos = 0;
55691                     locked = false;
55692                 }
55693                 pos += w;
55694                 s[i].style.left = (pos-this.splitOffset) + "px";
55695             }
55696         }
55697     },
55698
55699     handleHiddenChange : function(colModel, colIndex, hidden){
55700         if(hidden){
55701             this.hideColumn(colIndex);
55702         }else{
55703             this.unhideColumn(colIndex);
55704         }
55705     },
55706
55707     hideColumn : function(colIndex){
55708         var cid = this.getColumnId(colIndex);
55709         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55710         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55711         if(Roo.isSafari){
55712             this.updateHeaders();
55713         }
55714         this.updateSplitters();
55715         this.layout();
55716     },
55717
55718     unhideColumn : function(colIndex){
55719         var cid = this.getColumnId(colIndex);
55720         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55721         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55722
55723         if(Roo.isSafari){
55724             this.updateHeaders();
55725         }
55726         this.updateSplitters();
55727         this.layout();
55728     },
55729
55730     insertRows : function(dm, firstRow, lastRow, isUpdate){
55731         if(firstRow == 0 && lastRow == dm.getCount()-1){
55732             this.refresh();
55733         }else{
55734             if(!isUpdate){
55735                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55736             }
55737             var s = this.getScrollState();
55738             var markup = this.renderRows(firstRow, lastRow);
55739             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55740             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55741             this.restoreScroll(s);
55742             if(!isUpdate){
55743                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55744                 this.syncRowHeights(firstRow, lastRow);
55745                 this.stripeRows(firstRow);
55746                 this.layout();
55747             }
55748         }
55749     },
55750
55751     bufferRows : function(markup, target, index){
55752         var before = null, trows = target.rows, tbody = target.tBodies[0];
55753         if(index < trows.length){
55754             before = trows[index];
55755         }
55756         var b = document.createElement("div");
55757         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55758         var rows = b.firstChild.rows;
55759         for(var i = 0, len = rows.length; i < len; i++){
55760             if(before){
55761                 tbody.insertBefore(rows[0], before);
55762             }else{
55763                 tbody.appendChild(rows[0]);
55764             }
55765         }
55766         b.innerHTML = "";
55767         b = null;
55768     },
55769
55770     deleteRows : function(dm, firstRow, lastRow){
55771         if(dm.getRowCount()<1){
55772             this.fireEvent("beforerefresh", this);
55773             this.mainBody.update("");
55774             this.lockedBody.update("");
55775             this.fireEvent("refresh", this);
55776         }else{
55777             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55778             var bt = this.getBodyTable();
55779             var tbody = bt.firstChild;
55780             var rows = bt.rows;
55781             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55782                 tbody.removeChild(rows[firstRow]);
55783             }
55784             this.stripeRows(firstRow);
55785             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55786         }
55787     },
55788
55789     updateRows : function(dataSource, firstRow, lastRow){
55790         var s = this.getScrollState();
55791         this.refresh();
55792         this.restoreScroll(s);
55793     },
55794
55795     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55796         if(!noRefresh){
55797            this.refresh();
55798         }
55799         this.updateHeaderSortState();
55800     },
55801
55802     getScrollState : function(){
55803         
55804         var sb = this.scroller.dom;
55805         return {left: sb.scrollLeft, top: sb.scrollTop};
55806     },
55807
55808     stripeRows : function(startRow){
55809         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55810             return;
55811         }
55812         startRow = startRow || 0;
55813         var rows = this.getBodyTable().rows;
55814         var lrows = this.getLockedTable().rows;
55815         var cls = ' x-grid-row-alt ';
55816         for(var i = startRow, len = rows.length; i < len; i++){
55817             var row = rows[i], lrow = lrows[i];
55818             var isAlt = ((i+1) % 2 == 0);
55819             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55820             if(isAlt == hasAlt){
55821                 continue;
55822             }
55823             if(isAlt){
55824                 row.className += " x-grid-row-alt";
55825             }else{
55826                 row.className = row.className.replace("x-grid-row-alt", "");
55827             }
55828             if(lrow){
55829                 lrow.className = row.className;
55830             }
55831         }
55832     },
55833
55834     restoreScroll : function(state){
55835         //Roo.log('GridView.restoreScroll');
55836         var sb = this.scroller.dom;
55837         sb.scrollLeft = state.left;
55838         sb.scrollTop = state.top;
55839         this.syncScroll();
55840     },
55841
55842     syncScroll : function(){
55843         //Roo.log('GridView.syncScroll');
55844         var sb = this.scroller.dom;
55845         var sh = this.mainHd.dom;
55846         var bs = this.mainBody.dom;
55847         var lv = this.lockedBody.dom;
55848         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55849         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55850     },
55851
55852     handleScroll : function(e){
55853         this.syncScroll();
55854         var sb = this.scroller.dom;
55855         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55856         e.stopEvent();
55857     },
55858
55859     handleWheel : function(e){
55860         var d = e.getWheelDelta();
55861         this.scroller.dom.scrollTop -= d*22;
55862         // set this here to prevent jumpy scrolling on large tables
55863         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
55864         e.stopEvent();
55865     },
55866
55867     renderRows : function(startRow, endRow){
55868         // pull in all the crap needed to render rows
55869         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
55870         var colCount = cm.getColumnCount();
55871
55872         if(ds.getCount() < 1){
55873             return ["", ""];
55874         }
55875
55876         // build a map for all the columns
55877         var cs = [];
55878         for(var i = 0; i < colCount; i++){
55879             var name = cm.getDataIndex(i);
55880             cs[i] = {
55881                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
55882                 renderer : cm.getRenderer(i),
55883                 id : cm.getColumnId(i),
55884                 locked : cm.isLocked(i),
55885                 has_editor : cm.isCellEditable(i)
55886             };
55887         }
55888
55889         startRow = startRow || 0;
55890         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
55891
55892         // records to render
55893         var rs = ds.getRange(startRow, endRow);
55894
55895         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
55896     },
55897
55898     // As much as I hate to duplicate code, this was branched because FireFox really hates
55899     // [].join("") on strings. The performance difference was substantial enough to
55900     // branch this function
55901     doRender : Roo.isGecko ?
55902             function(cs, rs, ds, startRow, colCount, stripe){
55903                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55904                 // buffers
55905                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55906                 
55907                 var hasListener = this.grid.hasListener('rowclass');
55908                 var rowcfg = {};
55909                 for(var j = 0, len = rs.length; j < len; j++){
55910                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
55911                     for(var i = 0; i < colCount; i++){
55912                         c = cs[i];
55913                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55914                         p.id = c.id;
55915                         p.css = p.attr = "";
55916                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55917                         if(p.value == undefined || p.value === "") {
55918                             p.value = "&#160;";
55919                         }
55920                         if(c.has_editor){
55921                             p.css += ' x-grid-editable-cell';
55922                         }
55923                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
55924                             p.css +=  ' x-grid-dirty-cell';
55925                         }
55926                         var markup = ct.apply(p);
55927                         if(!c.locked){
55928                             cb+= markup;
55929                         }else{
55930                             lcb+= markup;
55931                         }
55932                     }
55933                     var alt = [];
55934                     if(stripe && ((rowIndex+1) % 2 == 0)){
55935                         alt.push("x-grid-row-alt")
55936                     }
55937                     if(r.dirty){
55938                         alt.push(  " x-grid-dirty-row");
55939                     }
55940                     rp.cells = lcb;
55941                     if(this.getRowClass){
55942                         alt.push(this.getRowClass(r, rowIndex));
55943                     }
55944                     if (hasListener) {
55945                         rowcfg = {
55946                              
55947                             record: r,
55948                             rowIndex : rowIndex,
55949                             rowClass : ''
55950                         };
55951                         this.grid.fireEvent('rowclass', this, rowcfg);
55952                         alt.push(rowcfg.rowClass);
55953                     }
55954                     rp.alt = alt.join(" ");
55955                     lbuf+= rt.apply(rp);
55956                     rp.cells = cb;
55957                     buf+=  rt.apply(rp);
55958                 }
55959                 return [lbuf, buf];
55960             } :
55961             function(cs, rs, ds, startRow, colCount, stripe){
55962                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55963                 // buffers
55964                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55965                 var hasListener = this.grid.hasListener('rowclass');
55966  
55967                 var rowcfg = {};
55968                 for(var j = 0, len = rs.length; j < len; j++){
55969                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
55970                     for(var i = 0; i < colCount; i++){
55971                         c = cs[i];
55972                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55973                         p.id = c.id;
55974                         p.css = p.attr = "";
55975                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55976                         if(p.value == undefined || p.value === "") {
55977                             p.value = "&#160;";
55978                         }
55979                         //Roo.log(c);
55980                          if(c.has_editor){
55981                             p.css += ' x-grid-editable-cell';
55982                         }
55983                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
55984                             p.css += ' x-grid-dirty-cell' 
55985                         }
55986                         
55987                         var markup = ct.apply(p);
55988                         if(!c.locked){
55989                             cb[cb.length] = markup;
55990                         }else{
55991                             lcb[lcb.length] = markup;
55992                         }
55993                     }
55994                     var alt = [];
55995                     if(stripe && ((rowIndex+1) % 2 == 0)){
55996                         alt.push( "x-grid-row-alt");
55997                     }
55998                     if(r.dirty){
55999                         alt.push(" x-grid-dirty-row");
56000                     }
56001                     rp.cells = lcb;
56002                     if(this.getRowClass){
56003                         alt.push( this.getRowClass(r, rowIndex));
56004                     }
56005                     if (hasListener) {
56006                         rowcfg = {
56007                              
56008                             record: r,
56009                             rowIndex : rowIndex,
56010                             rowClass : ''
56011                         };
56012                         this.grid.fireEvent('rowclass', this, rowcfg);
56013                         alt.push(rowcfg.rowClass);
56014                     }
56015                     
56016                     rp.alt = alt.join(" ");
56017                     rp.cells = lcb.join("");
56018                     lbuf[lbuf.length] = rt.apply(rp);
56019                     rp.cells = cb.join("");
56020                     buf[buf.length] =  rt.apply(rp);
56021                 }
56022                 return [lbuf.join(""), buf.join("")];
56023             },
56024
56025     renderBody : function(){
56026         var markup = this.renderRows();
56027         var bt = this.templates.body;
56028         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56029     },
56030
56031     /**
56032      * Refreshes the grid
56033      * @param {Boolean} headersToo
56034      */
56035     refresh : function(headersToo){
56036         this.fireEvent("beforerefresh", this);
56037         this.grid.stopEditing();
56038         var result = this.renderBody();
56039         this.lockedBody.update(result[0]);
56040         this.mainBody.update(result[1]);
56041         if(headersToo === true){
56042             this.updateHeaders();
56043             this.updateColumns();
56044             this.updateSplitters();
56045             this.updateHeaderSortState();
56046         }
56047         this.syncRowHeights();
56048         this.layout();
56049         this.fireEvent("refresh", this);
56050     },
56051
56052     handleColumnMove : function(cm, oldIndex, newIndex){
56053         this.indexMap = null;
56054         var s = this.getScrollState();
56055         this.refresh(true);
56056         this.restoreScroll(s);
56057         this.afterMove(newIndex);
56058     },
56059
56060     afterMove : function(colIndex){
56061         if(this.enableMoveAnim && Roo.enableFx){
56062             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56063         }
56064         // if multisort - fix sortOrder, and reload..
56065         if (this.grid.dataSource.multiSort) {
56066             // the we can call sort again..
56067             var dm = this.grid.dataSource;
56068             var cm = this.grid.colModel;
56069             var so = [];
56070             for(var i = 0; i < cm.config.length; i++ ) {
56071                 
56072                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56073                     continue; // dont' bother, it's not in sort list or being set.
56074                 }
56075                 
56076                 so.push(cm.config[i].dataIndex);
56077             };
56078             dm.sortOrder = so;
56079             dm.load(dm.lastOptions);
56080             
56081             
56082         }
56083         
56084     },
56085
56086     updateCell : function(dm, rowIndex, dataIndex){
56087         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56088         if(typeof colIndex == "undefined"){ // not present in grid
56089             return;
56090         }
56091         var cm = this.grid.colModel;
56092         var cell = this.getCell(rowIndex, colIndex);
56093         var cellText = this.getCellText(rowIndex, colIndex);
56094
56095         var p = {
56096             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56097             id : cm.getColumnId(colIndex),
56098             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56099         };
56100         var renderer = cm.getRenderer(colIndex);
56101         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56102         if(typeof val == "undefined" || val === "") {
56103             val = "&#160;";
56104         }
56105         cellText.innerHTML = val;
56106         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56107         this.syncRowHeights(rowIndex, rowIndex);
56108     },
56109
56110     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56111         var maxWidth = 0;
56112         if(this.grid.autoSizeHeaders){
56113             var h = this.getHeaderCellMeasure(colIndex);
56114             maxWidth = Math.max(maxWidth, h.scrollWidth);
56115         }
56116         var tb, index;
56117         if(this.cm.isLocked(colIndex)){
56118             tb = this.getLockedTable();
56119             index = colIndex;
56120         }else{
56121             tb = this.getBodyTable();
56122             index = colIndex - this.cm.getLockedCount();
56123         }
56124         if(tb && tb.rows){
56125             var rows = tb.rows;
56126             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56127             for(var i = 0; i < stopIndex; i++){
56128                 var cell = rows[i].childNodes[index].firstChild;
56129                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56130             }
56131         }
56132         return maxWidth + /*margin for error in IE*/ 5;
56133     },
56134     /**
56135      * Autofit a column to its content.
56136      * @param {Number} colIndex
56137      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56138      */
56139      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56140          if(this.cm.isHidden(colIndex)){
56141              return; // can't calc a hidden column
56142          }
56143         if(forceMinSize){
56144             var cid = this.cm.getColumnId(colIndex);
56145             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56146            if(this.grid.autoSizeHeaders){
56147                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56148            }
56149         }
56150         var newWidth = this.calcColumnWidth(colIndex);
56151         this.cm.setColumnWidth(colIndex,
56152             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56153         if(!suppressEvent){
56154             this.grid.fireEvent("columnresize", colIndex, newWidth);
56155         }
56156     },
56157
56158     /**
56159      * Autofits all columns to their content and then expands to fit any extra space in the grid
56160      */
56161      autoSizeColumns : function(){
56162         var cm = this.grid.colModel;
56163         var colCount = cm.getColumnCount();
56164         for(var i = 0; i < colCount; i++){
56165             this.autoSizeColumn(i, true, true);
56166         }
56167         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56168             this.fitColumns();
56169         }else{
56170             this.updateColumns();
56171             this.layout();
56172         }
56173     },
56174
56175     /**
56176      * Autofits all columns to the grid's width proportionate with their current size
56177      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56178      */
56179     fitColumns : function(reserveScrollSpace){
56180         var cm = this.grid.colModel;
56181         var colCount = cm.getColumnCount();
56182         var cols = [];
56183         var width = 0;
56184         var i, w;
56185         for (i = 0; i < colCount; i++){
56186             if(!cm.isHidden(i) && !cm.isFixed(i)){
56187                 w = cm.getColumnWidth(i);
56188                 cols.push(i);
56189                 cols.push(w);
56190                 width += w;
56191             }
56192         }
56193         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56194         if(reserveScrollSpace){
56195             avail -= 17;
56196         }
56197         var frac = (avail - cm.getTotalWidth())/width;
56198         while (cols.length){
56199             w = cols.pop();
56200             i = cols.pop();
56201             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56202         }
56203         this.updateColumns();
56204         this.layout();
56205     },
56206
56207     onRowSelect : function(rowIndex){
56208         var row = this.getRowComposite(rowIndex);
56209         row.addClass("x-grid-row-selected");
56210     },
56211
56212     onRowDeselect : function(rowIndex){
56213         var row = this.getRowComposite(rowIndex);
56214         row.removeClass("x-grid-row-selected");
56215     },
56216
56217     onCellSelect : function(row, col){
56218         var cell = this.getCell(row, col);
56219         if(cell){
56220             Roo.fly(cell).addClass("x-grid-cell-selected");
56221         }
56222     },
56223
56224     onCellDeselect : function(row, col){
56225         var cell = this.getCell(row, col);
56226         if(cell){
56227             Roo.fly(cell).removeClass("x-grid-cell-selected");
56228         }
56229     },
56230
56231     updateHeaderSortState : function(){
56232         
56233         // sort state can be single { field: xxx, direction : yyy}
56234         // or   { xxx=>ASC , yyy : DESC ..... }
56235         
56236         var mstate = {};
56237         if (!this.ds.multiSort) { 
56238             var state = this.ds.getSortState();
56239             if(!state){
56240                 return;
56241             }
56242             mstate[state.field] = state.direction;
56243             // FIXME... - this is not used here.. but might be elsewhere..
56244             this.sortState = state;
56245             
56246         } else {
56247             mstate = this.ds.sortToggle;
56248         }
56249         //remove existing sort classes..
56250         
56251         var sc = this.sortClasses;
56252         var hds = this.el.select(this.headerSelector).removeClass(sc);
56253         
56254         for(var f in mstate) {
56255         
56256             var sortColumn = this.cm.findColumnIndex(f);
56257             
56258             if(sortColumn != -1){
56259                 var sortDir = mstate[f];        
56260                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56261             }
56262         }
56263         
56264          
56265         
56266     },
56267
56268
56269     handleHeaderClick : function(g, index,e){
56270         
56271         Roo.log("header click");
56272         
56273         if (Roo.isTouch) {
56274             // touch events on header are handled by context
56275             this.handleHdCtx(g,index,e);
56276             return;
56277         }
56278         
56279         
56280         if(this.headersDisabled){
56281             return;
56282         }
56283         var dm = g.dataSource, cm = g.colModel;
56284         if(!cm.isSortable(index)){
56285             return;
56286         }
56287         g.stopEditing();
56288         
56289         if (dm.multiSort) {
56290             // update the sortOrder
56291             var so = [];
56292             for(var i = 0; i < cm.config.length; i++ ) {
56293                 
56294                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56295                     continue; // dont' bother, it's not in sort list or being set.
56296                 }
56297                 
56298                 so.push(cm.config[i].dataIndex);
56299             };
56300             dm.sortOrder = so;
56301         }
56302         
56303         
56304         dm.sort(cm.getDataIndex(index));
56305     },
56306
56307
56308     destroy : function(){
56309         if(this.colMenu){
56310             this.colMenu.removeAll();
56311             Roo.menu.MenuMgr.unregister(this.colMenu);
56312             this.colMenu.getEl().remove();
56313             delete this.colMenu;
56314         }
56315         if(this.hmenu){
56316             this.hmenu.removeAll();
56317             Roo.menu.MenuMgr.unregister(this.hmenu);
56318             this.hmenu.getEl().remove();
56319             delete this.hmenu;
56320         }
56321         if(this.grid.enableColumnMove){
56322             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56323             if(dds){
56324                 for(var dd in dds){
56325                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56326                         var elid = dds[dd].dragElId;
56327                         dds[dd].unreg();
56328                         Roo.get(elid).remove();
56329                     } else if(dds[dd].config.isTarget){
56330                         dds[dd].proxyTop.remove();
56331                         dds[dd].proxyBottom.remove();
56332                         dds[dd].unreg();
56333                     }
56334                     if(Roo.dd.DDM.locationCache[dd]){
56335                         delete Roo.dd.DDM.locationCache[dd];
56336                     }
56337                 }
56338                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56339             }
56340         }
56341         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56342         this.bind(null, null);
56343         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56344     },
56345
56346     handleLockChange : function(){
56347         this.refresh(true);
56348     },
56349
56350     onDenyColumnLock : function(){
56351
56352     },
56353
56354     onDenyColumnHide : function(){
56355
56356     },
56357
56358     handleHdMenuClick : function(item){
56359         var index = this.hdCtxIndex;
56360         var cm = this.cm, ds = this.ds;
56361         switch(item.id){
56362             case "asc":
56363                 ds.sort(cm.getDataIndex(index), "ASC");
56364                 break;
56365             case "desc":
56366                 ds.sort(cm.getDataIndex(index), "DESC");
56367                 break;
56368             case "lock":
56369                 var lc = cm.getLockedCount();
56370                 if(cm.getColumnCount(true) <= lc+1){
56371                     this.onDenyColumnLock();
56372                     return;
56373                 }
56374                 if(lc != index){
56375                     cm.setLocked(index, true, true);
56376                     cm.moveColumn(index, lc);
56377                     this.grid.fireEvent("columnmove", index, lc);
56378                 }else{
56379                     cm.setLocked(index, true);
56380                 }
56381             break;
56382             case "unlock":
56383                 var lc = cm.getLockedCount();
56384                 if((lc-1) != index){
56385                     cm.setLocked(index, false, true);
56386                     cm.moveColumn(index, lc-1);
56387                     this.grid.fireEvent("columnmove", index, lc-1);
56388                 }else{
56389                     cm.setLocked(index, false);
56390                 }
56391             break;
56392             case 'wider': // used to expand cols on touch..
56393             case 'narrow':
56394                 var cw = cm.getColumnWidth(index);
56395                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56396                 cw = Math.max(0, cw);
56397                 cw = Math.min(cw,4000);
56398                 cm.setColumnWidth(index, cw);
56399                 break;
56400                 
56401             default:
56402                 index = cm.getIndexById(item.id.substr(4));
56403                 if(index != -1){
56404                     if(item.checked && cm.getColumnCount(true) <= 1){
56405                         this.onDenyColumnHide();
56406                         return false;
56407                     }
56408                     cm.setHidden(index, item.checked);
56409                 }
56410         }
56411         return true;
56412     },
56413
56414     beforeColMenuShow : function(){
56415         var cm = this.cm,  colCount = cm.getColumnCount();
56416         this.colMenu.removeAll();
56417         for(var i = 0; i < colCount; i++){
56418             this.colMenu.add(new Roo.menu.CheckItem({
56419                 id: "col-"+cm.getColumnId(i),
56420                 text: cm.getColumnHeader(i),
56421                 checked: !cm.isHidden(i),
56422                 hideOnClick:false
56423             }));
56424         }
56425     },
56426
56427     handleHdCtx : function(g, index, e){
56428         e.stopEvent();
56429         var hd = this.getHeaderCell(index);
56430         this.hdCtxIndex = index;
56431         var ms = this.hmenu.items, cm = this.cm;
56432         ms.get("asc").setDisabled(!cm.isSortable(index));
56433         ms.get("desc").setDisabled(!cm.isSortable(index));
56434         if(this.grid.enableColLock !== false){
56435             ms.get("lock").setDisabled(cm.isLocked(index));
56436             ms.get("unlock").setDisabled(!cm.isLocked(index));
56437         }
56438         this.hmenu.show(hd, "tl-bl");
56439     },
56440
56441     handleHdOver : function(e){
56442         var hd = this.findHeaderCell(e.getTarget());
56443         if(hd && !this.headersDisabled){
56444             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56445                this.fly(hd).addClass("x-grid-hd-over");
56446             }
56447         }
56448     },
56449
56450     handleHdOut : function(e){
56451         var hd = this.findHeaderCell(e.getTarget());
56452         if(hd){
56453             this.fly(hd).removeClass("x-grid-hd-over");
56454         }
56455     },
56456
56457     handleSplitDblClick : function(e, t){
56458         var i = this.getCellIndex(t);
56459         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56460             this.autoSizeColumn(i, true);
56461             this.layout();
56462         }
56463     },
56464
56465     render : function(){
56466
56467         var cm = this.cm;
56468         var colCount = cm.getColumnCount();
56469
56470         if(this.grid.monitorWindowResize === true){
56471             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56472         }
56473         var header = this.renderHeaders();
56474         var body = this.templates.body.apply({rows:""});
56475         var html = this.templates.master.apply({
56476             lockedBody: body,
56477             body: body,
56478             lockedHeader: header[0],
56479             header: header[1]
56480         });
56481
56482         //this.updateColumns();
56483
56484         this.grid.getGridEl().dom.innerHTML = html;
56485
56486         this.initElements();
56487         
56488         // a kludge to fix the random scolling effect in webkit
56489         this.el.on("scroll", function() {
56490             this.el.dom.scrollTop=0; // hopefully not recursive..
56491         },this);
56492
56493         this.scroller.on("scroll", this.handleScroll, this);
56494         this.lockedBody.on("mousewheel", this.handleWheel, this);
56495         this.mainBody.on("mousewheel", this.handleWheel, this);
56496
56497         this.mainHd.on("mouseover", this.handleHdOver, this);
56498         this.mainHd.on("mouseout", this.handleHdOut, this);
56499         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56500                 {delegate: "."+this.splitClass});
56501
56502         this.lockedHd.on("mouseover", this.handleHdOver, this);
56503         this.lockedHd.on("mouseout", this.handleHdOut, this);
56504         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56505                 {delegate: "."+this.splitClass});
56506
56507         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56508             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56509         }
56510
56511         this.updateSplitters();
56512
56513         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56514             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56515             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56516         }
56517
56518         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56519             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56520             this.hmenu.add(
56521                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56522                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56523             );
56524             if(this.grid.enableColLock !== false){
56525                 this.hmenu.add('-',
56526                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56527                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56528                 );
56529             }
56530             if (Roo.isTouch) {
56531                  this.hmenu.add('-',
56532                     {id:"wider", text: this.columnsWiderText},
56533                     {id:"narrow", text: this.columnsNarrowText }
56534                 );
56535                 
56536                  
56537             }
56538             
56539             if(this.grid.enableColumnHide !== false){
56540
56541                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56542                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56543                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56544
56545                 this.hmenu.add('-',
56546                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56547                 );
56548             }
56549             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56550
56551             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56552         }
56553
56554         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56555             this.dd = new Roo.grid.GridDragZone(this.grid, {
56556                 ddGroup : this.grid.ddGroup || 'GridDD'
56557             });
56558             
56559         }
56560
56561         /*
56562         for(var i = 0; i < colCount; i++){
56563             if(cm.isHidden(i)){
56564                 this.hideColumn(i);
56565             }
56566             if(cm.config[i].align){
56567                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56568                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56569             }
56570         }*/
56571         
56572         this.updateHeaderSortState();
56573
56574         this.beforeInitialResize();
56575         this.layout(true);
56576
56577         // two part rendering gives faster view to the user
56578         this.renderPhase2.defer(1, this);
56579     },
56580
56581     renderPhase2 : function(){
56582         // render the rows now
56583         this.refresh();
56584         if(this.grid.autoSizeColumns){
56585             this.autoSizeColumns();
56586         }
56587     },
56588
56589     beforeInitialResize : function(){
56590
56591     },
56592
56593     onColumnSplitterMoved : function(i, w){
56594         this.userResized = true;
56595         var cm = this.grid.colModel;
56596         cm.setColumnWidth(i, w, true);
56597         var cid = cm.getColumnId(i);
56598         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56599         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56600         this.updateSplitters();
56601         this.layout();
56602         this.grid.fireEvent("columnresize", i, w);
56603     },
56604
56605     syncRowHeights : function(startIndex, endIndex){
56606         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56607             startIndex = startIndex || 0;
56608             var mrows = this.getBodyTable().rows;
56609             var lrows = this.getLockedTable().rows;
56610             var len = mrows.length-1;
56611             endIndex = Math.min(endIndex || len, len);
56612             for(var i = startIndex; i <= endIndex; i++){
56613                 var m = mrows[i], l = lrows[i];
56614                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56615                 m.style.height = l.style.height = h + "px";
56616             }
56617         }
56618     },
56619
56620     layout : function(initialRender, is2ndPass){
56621         var g = this.grid;
56622         var auto = g.autoHeight;
56623         var scrollOffset = 16;
56624         var c = g.getGridEl(), cm = this.cm,
56625                 expandCol = g.autoExpandColumn,
56626                 gv = this;
56627         //c.beginMeasure();
56628
56629         if(!c.dom.offsetWidth){ // display:none?
56630             if(initialRender){
56631                 this.lockedWrap.show();
56632                 this.mainWrap.show();
56633             }
56634             return;
56635         }
56636
56637         var hasLock = this.cm.isLocked(0);
56638
56639         var tbh = this.headerPanel.getHeight();
56640         var bbh = this.footerPanel.getHeight();
56641
56642         if(auto){
56643             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56644             var newHeight = ch + c.getBorderWidth("tb");
56645             if(g.maxHeight){
56646                 newHeight = Math.min(g.maxHeight, newHeight);
56647             }
56648             c.setHeight(newHeight);
56649         }
56650
56651         if(g.autoWidth){
56652             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56653         }
56654
56655         var s = this.scroller;
56656
56657         var csize = c.getSize(true);
56658
56659         this.el.setSize(csize.width, csize.height);
56660
56661         this.headerPanel.setWidth(csize.width);
56662         this.footerPanel.setWidth(csize.width);
56663
56664         var hdHeight = this.mainHd.getHeight();
56665         var vw = csize.width;
56666         var vh = csize.height - (tbh + bbh);
56667
56668         s.setSize(vw, vh);
56669
56670         var bt = this.getBodyTable();
56671         
56672         if(cm.getLockedCount() == cm.config.length){
56673             bt = this.getLockedTable();
56674         }
56675         
56676         var ltWidth = hasLock ?
56677                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56678
56679         var scrollHeight = bt.offsetHeight;
56680         var scrollWidth = ltWidth + bt.offsetWidth;
56681         var vscroll = false, hscroll = false;
56682
56683         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56684
56685         var lw = this.lockedWrap, mw = this.mainWrap;
56686         var lb = this.lockedBody, mb = this.mainBody;
56687
56688         setTimeout(function(){
56689             var t = s.dom.offsetTop;
56690             var w = s.dom.clientWidth,
56691                 h = s.dom.clientHeight;
56692
56693             lw.setTop(t);
56694             lw.setSize(ltWidth, h);
56695
56696             mw.setLeftTop(ltWidth, t);
56697             mw.setSize(w-ltWidth, h);
56698
56699             lb.setHeight(h-hdHeight);
56700             mb.setHeight(h-hdHeight);
56701
56702             if(is2ndPass !== true && !gv.userResized && expandCol){
56703                 // high speed resize without full column calculation
56704                 
56705                 var ci = cm.getIndexById(expandCol);
56706                 if (ci < 0) {
56707                     ci = cm.findColumnIndex(expandCol);
56708                 }
56709                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56710                 var expandId = cm.getColumnId(ci);
56711                 var  tw = cm.getTotalWidth(false);
56712                 var currentWidth = cm.getColumnWidth(ci);
56713                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56714                 if(currentWidth != cw){
56715                     cm.setColumnWidth(ci, cw, true);
56716                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56717                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56718                     gv.updateSplitters();
56719                     gv.layout(false, true);
56720                 }
56721             }
56722
56723             if(initialRender){
56724                 lw.show();
56725                 mw.show();
56726             }
56727             //c.endMeasure();
56728         }, 10);
56729     },
56730
56731     onWindowResize : function(){
56732         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56733             return;
56734         }
56735         this.layout();
56736     },
56737
56738     appendFooter : function(parentEl){
56739         return null;
56740     },
56741
56742     sortAscText : "Sort Ascending",
56743     sortDescText : "Sort Descending",
56744     lockText : "Lock Column",
56745     unlockText : "Unlock Column",
56746     columnsText : "Columns",
56747  
56748     columnsWiderText : "Wider",
56749     columnsNarrowText : "Thinner"
56750 });
56751
56752
56753 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56754     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56755     this.proxy.el.addClass('x-grid3-col-dd');
56756 };
56757
56758 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56759     handleMouseDown : function(e){
56760
56761     },
56762
56763     callHandleMouseDown : function(e){
56764         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56765     }
56766 });
56767 /*
56768  * Based on:
56769  * Ext JS Library 1.1.1
56770  * Copyright(c) 2006-2007, Ext JS, LLC.
56771  *
56772  * Originally Released Under LGPL - original licence link has changed is not relivant.
56773  *
56774  * Fork - LGPL
56775  * <script type="text/javascript">
56776  */
56777  
56778 // private
56779 // This is a support class used internally by the Grid components
56780 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56781     this.grid = grid;
56782     this.view = grid.getView();
56783     this.proxy = this.view.resizeProxy;
56784     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56785         "gridSplitters" + this.grid.getGridEl().id, {
56786         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56787     });
56788     this.setHandleElId(Roo.id(hd));
56789     this.setOuterHandleElId(Roo.id(hd2));
56790     this.scroll = false;
56791 };
56792 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56793     fly: Roo.Element.fly,
56794
56795     b4StartDrag : function(x, y){
56796         this.view.headersDisabled = true;
56797         this.proxy.setHeight(this.view.mainWrap.getHeight());
56798         var w = this.cm.getColumnWidth(this.cellIndex);
56799         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56800         this.resetConstraints();
56801         this.setXConstraint(minw, 1000);
56802         this.setYConstraint(0, 0);
56803         this.minX = x - minw;
56804         this.maxX = x + 1000;
56805         this.startPos = x;
56806         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56807     },
56808
56809
56810     handleMouseDown : function(e){
56811         ev = Roo.EventObject.setEvent(e);
56812         var t = this.fly(ev.getTarget());
56813         if(t.hasClass("x-grid-split")){
56814             this.cellIndex = this.view.getCellIndex(t.dom);
56815             this.split = t.dom;
56816             this.cm = this.grid.colModel;
56817             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56818                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56819             }
56820         }
56821     },
56822
56823     endDrag : function(e){
56824         this.view.headersDisabled = false;
56825         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56826         var diff = endX - this.startPos;
56827         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56828     },
56829
56830     autoOffset : function(){
56831         this.setDelta(0,0);
56832     }
56833 });/*
56834  * Based on:
56835  * Ext JS Library 1.1.1
56836  * Copyright(c) 2006-2007, Ext JS, LLC.
56837  *
56838  * Originally Released Under LGPL - original licence link has changed is not relivant.
56839  *
56840  * Fork - LGPL
56841  * <script type="text/javascript">
56842  */
56843  
56844 // private
56845 // This is a support class used internally by the Grid components
56846 Roo.grid.GridDragZone = function(grid, config){
56847     this.view = grid.getView();
56848     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56849     if(this.view.lockedBody){
56850         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56851         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56852     }
56853     this.scroll = false;
56854     this.grid = grid;
56855     this.ddel = document.createElement('div');
56856     this.ddel.className = 'x-grid-dd-wrap';
56857 };
56858
56859 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56860     ddGroup : "GridDD",
56861
56862     getDragData : function(e){
56863         var t = Roo.lib.Event.getTarget(e);
56864         var rowIndex = this.view.findRowIndex(t);
56865         var sm = this.grid.selModel;
56866             
56867         //Roo.log(rowIndex);
56868         
56869         if (sm.getSelectedCell) {
56870             // cell selection..
56871             if (!sm.getSelectedCell()) {
56872                 return false;
56873             }
56874             if (rowIndex != sm.getSelectedCell()[0]) {
56875                 return false;
56876             }
56877         
56878         }
56879         
56880         if(rowIndex !== false){
56881             
56882             // if editorgrid.. 
56883             
56884             
56885             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
56886                
56887             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
56888               //  
56889             //}
56890             if (e.hasModifier()){
56891                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
56892             }
56893             
56894             Roo.log("getDragData");
56895             
56896             return {
56897                 grid: this.grid,
56898                 ddel: this.ddel,
56899                 rowIndex: rowIndex,
56900                 selections:sm.getSelections ? sm.getSelections() : (
56901                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
56902                 )
56903             };
56904         }
56905         return false;
56906     },
56907
56908     onInitDrag : function(e){
56909         var data = this.dragData;
56910         this.ddel.innerHTML = this.grid.getDragDropText();
56911         this.proxy.update(this.ddel);
56912         // fire start drag?
56913     },
56914
56915     afterRepair : function(){
56916         this.dragging = false;
56917     },
56918
56919     getRepairXY : function(e, data){
56920         return false;
56921     },
56922
56923     onEndDrag : function(data, e){
56924         // fire end drag?
56925     },
56926
56927     onValidDrop : function(dd, e, id){
56928         // fire drag drop?
56929         this.hideProxy();
56930     },
56931
56932     beforeInvalidDrop : function(e, id){
56933
56934     }
56935 });/*
56936  * Based on:
56937  * Ext JS Library 1.1.1
56938  * Copyright(c) 2006-2007, Ext JS, LLC.
56939  *
56940  * Originally Released Under LGPL - original licence link has changed is not relivant.
56941  *
56942  * Fork - LGPL
56943  * <script type="text/javascript">
56944  */
56945  
56946
56947 /**
56948  * @class Roo.grid.ColumnModel
56949  * @extends Roo.util.Observable
56950  * This is the default implementation of a ColumnModel used by the Grid. It defines
56951  * the columns in the grid.
56952  * <br>Usage:<br>
56953  <pre><code>
56954  var colModel = new Roo.grid.ColumnModel([
56955         {header: "Ticker", width: 60, sortable: true, locked: true},
56956         {header: "Company Name", width: 150, sortable: true},
56957         {header: "Market Cap.", width: 100, sortable: true},
56958         {header: "$ Sales", width: 100, sortable: true, renderer: money},
56959         {header: "Employees", width: 100, sortable: true, resizable: false}
56960  ]);
56961  </code></pre>
56962  * <p>
56963  
56964  * The config options listed for this class are options which may appear in each
56965  * individual column definition.
56966  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
56967  * @constructor
56968  * @param {Object} config An Array of column config objects. See this class's
56969  * config objects for details.
56970 */
56971 Roo.grid.ColumnModel = function(config){
56972         /**
56973      * The config passed into the constructor
56974      */
56975     this.config = config;
56976     this.lookup = {};
56977
56978     // if no id, create one
56979     // if the column does not have a dataIndex mapping,
56980     // map it to the order it is in the config
56981     for(var i = 0, len = config.length; i < len; i++){
56982         var c = config[i];
56983         if(typeof c.dataIndex == "undefined"){
56984             c.dataIndex = i;
56985         }
56986         if(typeof c.renderer == "string"){
56987             c.renderer = Roo.util.Format[c.renderer];
56988         }
56989         if(typeof c.id == "undefined"){
56990             c.id = Roo.id();
56991         }
56992         if(c.editor && c.editor.xtype){
56993             c.editor  = Roo.factory(c.editor, Roo.grid);
56994         }
56995         if(c.editor && c.editor.isFormField){
56996             c.editor = new Roo.grid.GridEditor(c.editor);
56997         }
56998         this.lookup[c.id] = c;
56999     }
57000
57001     /**
57002      * The width of columns which have no width specified (defaults to 100)
57003      * @type Number
57004      */
57005     this.defaultWidth = 100;
57006
57007     /**
57008      * Default sortable of columns which have no sortable specified (defaults to false)
57009      * @type Boolean
57010      */
57011     this.defaultSortable = false;
57012
57013     this.addEvents({
57014         /**
57015              * @event widthchange
57016              * Fires when the width of a column changes.
57017              * @param {ColumnModel} this
57018              * @param {Number} columnIndex The column index
57019              * @param {Number} newWidth The new width
57020              */
57021             "widthchange": true,
57022         /**
57023              * @event headerchange
57024              * Fires when the text of a header changes.
57025              * @param {ColumnModel} this
57026              * @param {Number} columnIndex The column index
57027              * @param {Number} newText The new header text
57028              */
57029             "headerchange": true,
57030         /**
57031              * @event hiddenchange
57032              * Fires when a column is hidden or "unhidden".
57033              * @param {ColumnModel} this
57034              * @param {Number} columnIndex The column index
57035              * @param {Boolean} hidden true if hidden, false otherwise
57036              */
57037             "hiddenchange": true,
57038             /**
57039          * @event columnmoved
57040          * Fires when a column is moved.
57041          * @param {ColumnModel} this
57042          * @param {Number} oldIndex
57043          * @param {Number} newIndex
57044          */
57045         "columnmoved" : true,
57046         /**
57047          * @event columlockchange
57048          * Fires when a column's locked state is changed
57049          * @param {ColumnModel} this
57050          * @param {Number} colIndex
57051          * @param {Boolean} locked true if locked
57052          */
57053         "columnlockchange" : true
57054     });
57055     Roo.grid.ColumnModel.superclass.constructor.call(this);
57056 };
57057 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57058     /**
57059      * @cfg {String} header The header text to display in the Grid view.
57060      */
57061     /**
57062      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57063      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57064      * specified, the column's index is used as an index into the Record's data Array.
57065      */
57066     /**
57067      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57068      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57069      */
57070     /**
57071      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57072      * Defaults to the value of the {@link #defaultSortable} property.
57073      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57074      */
57075     /**
57076      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57077      */
57078     /**
57079      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57080      */
57081     /**
57082      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57083      */
57084     /**
57085      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57086      */
57087     /**
57088      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57089      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57090      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57091      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57092      */
57093        /**
57094      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57095      */
57096     /**
57097      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57098      */
57099     /**
57100      * @cfg {String} cursor (Optional)
57101      */
57102     /**
57103      * @cfg {String} tooltip (Optional)
57104      */
57105     /**
57106      * @cfg {Number} xs (Optional)
57107      */
57108     /**
57109      * @cfg {Number} sm (Optional)
57110      */
57111     /**
57112      * @cfg {Number} md (Optional)
57113      */
57114     /**
57115      * @cfg {Number} lg (Optional)
57116      */
57117     /**
57118      * Returns the id of the column at the specified index.
57119      * @param {Number} index The column index
57120      * @return {String} the id
57121      */
57122     getColumnId : function(index){
57123         return this.config[index].id;
57124     },
57125
57126     /**
57127      * Returns the column for a specified id.
57128      * @param {String} id The column id
57129      * @return {Object} the column
57130      */
57131     getColumnById : function(id){
57132         return this.lookup[id];
57133     },
57134
57135     
57136     /**
57137      * Returns the column for a specified dataIndex.
57138      * @param {String} dataIndex The column dataIndex
57139      * @return {Object|Boolean} the column or false if not found
57140      */
57141     getColumnByDataIndex: function(dataIndex){
57142         var index = this.findColumnIndex(dataIndex);
57143         return index > -1 ? this.config[index] : false;
57144     },
57145     
57146     /**
57147      * Returns the index for a specified column id.
57148      * @param {String} id The column id
57149      * @return {Number} the index, or -1 if not found
57150      */
57151     getIndexById : function(id){
57152         for(var i = 0, len = this.config.length; i < len; i++){
57153             if(this.config[i].id == id){
57154                 return i;
57155             }
57156         }
57157         return -1;
57158     },
57159     
57160     /**
57161      * Returns the index for a specified column dataIndex.
57162      * @param {String} dataIndex The column dataIndex
57163      * @return {Number} the index, or -1 if not found
57164      */
57165     
57166     findColumnIndex : function(dataIndex){
57167         for(var i = 0, len = this.config.length; i < len; i++){
57168             if(this.config[i].dataIndex == dataIndex){
57169                 return i;
57170             }
57171         }
57172         return -1;
57173     },
57174     
57175     
57176     moveColumn : function(oldIndex, newIndex){
57177         var c = this.config[oldIndex];
57178         this.config.splice(oldIndex, 1);
57179         this.config.splice(newIndex, 0, c);
57180         this.dataMap = null;
57181         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57182     },
57183
57184     isLocked : function(colIndex){
57185         return this.config[colIndex].locked === true;
57186     },
57187
57188     setLocked : function(colIndex, value, suppressEvent){
57189         if(this.isLocked(colIndex) == value){
57190             return;
57191         }
57192         this.config[colIndex].locked = value;
57193         if(!suppressEvent){
57194             this.fireEvent("columnlockchange", this, colIndex, value);
57195         }
57196     },
57197
57198     getTotalLockedWidth : function(){
57199         var totalWidth = 0;
57200         for(var i = 0; i < this.config.length; i++){
57201             if(this.isLocked(i) && !this.isHidden(i)){
57202                 this.totalWidth += this.getColumnWidth(i);
57203             }
57204         }
57205         return totalWidth;
57206     },
57207
57208     getLockedCount : function(){
57209         for(var i = 0, len = this.config.length; i < len; i++){
57210             if(!this.isLocked(i)){
57211                 return i;
57212             }
57213         }
57214         
57215         return this.config.length;
57216     },
57217
57218     /**
57219      * Returns the number of columns.
57220      * @return {Number}
57221      */
57222     getColumnCount : function(visibleOnly){
57223         if(visibleOnly === true){
57224             var c = 0;
57225             for(var i = 0, len = this.config.length; i < len; i++){
57226                 if(!this.isHidden(i)){
57227                     c++;
57228                 }
57229             }
57230             return c;
57231         }
57232         return this.config.length;
57233     },
57234
57235     /**
57236      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57237      * @param {Function} fn
57238      * @param {Object} scope (optional)
57239      * @return {Array} result
57240      */
57241     getColumnsBy : function(fn, scope){
57242         var r = [];
57243         for(var i = 0, len = this.config.length; i < len; i++){
57244             var c = this.config[i];
57245             if(fn.call(scope||this, c, i) === true){
57246                 r[r.length] = c;
57247             }
57248         }
57249         return r;
57250     },
57251
57252     /**
57253      * Returns true if the specified column is sortable.
57254      * @param {Number} col The column index
57255      * @return {Boolean}
57256      */
57257     isSortable : function(col){
57258         if(typeof this.config[col].sortable == "undefined"){
57259             return this.defaultSortable;
57260         }
57261         return this.config[col].sortable;
57262     },
57263
57264     /**
57265      * Returns the rendering (formatting) function defined for the column.
57266      * @param {Number} col The column index.
57267      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57268      */
57269     getRenderer : function(col){
57270         if(!this.config[col].renderer){
57271             return Roo.grid.ColumnModel.defaultRenderer;
57272         }
57273         return this.config[col].renderer;
57274     },
57275
57276     /**
57277      * Sets the rendering (formatting) function for a column.
57278      * @param {Number} col The column index
57279      * @param {Function} fn The function to use to process the cell's raw data
57280      * to return HTML markup for the grid view. The render function is called with
57281      * the following parameters:<ul>
57282      * <li>Data value.</li>
57283      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57284      * <li>css A CSS style string to apply to the table cell.</li>
57285      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57286      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57287      * <li>Row index</li>
57288      * <li>Column index</li>
57289      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57290      */
57291     setRenderer : function(col, fn){
57292         this.config[col].renderer = fn;
57293     },
57294
57295     /**
57296      * Returns the width for the specified column.
57297      * @param {Number} col The column index
57298      * @return {Number}
57299      */
57300     getColumnWidth : function(col){
57301         return this.config[col].width * 1 || this.defaultWidth;
57302     },
57303
57304     /**
57305      * Sets the width for a column.
57306      * @param {Number} col The column index
57307      * @param {Number} width The new width
57308      */
57309     setColumnWidth : function(col, width, suppressEvent){
57310         this.config[col].width = width;
57311         this.totalWidth = null;
57312         if(!suppressEvent){
57313              this.fireEvent("widthchange", this, col, width);
57314         }
57315     },
57316
57317     /**
57318      * Returns the total width of all columns.
57319      * @param {Boolean} includeHidden True to include hidden column widths
57320      * @return {Number}
57321      */
57322     getTotalWidth : function(includeHidden){
57323         if(!this.totalWidth){
57324             this.totalWidth = 0;
57325             for(var i = 0, len = this.config.length; i < len; i++){
57326                 if(includeHidden || !this.isHidden(i)){
57327                     this.totalWidth += this.getColumnWidth(i);
57328                 }
57329             }
57330         }
57331         return this.totalWidth;
57332     },
57333
57334     /**
57335      * Returns the header for the specified column.
57336      * @param {Number} col The column index
57337      * @return {String}
57338      */
57339     getColumnHeader : function(col){
57340         return this.config[col].header;
57341     },
57342
57343     /**
57344      * Sets the header for a column.
57345      * @param {Number} col The column index
57346      * @param {String} header The new header
57347      */
57348     setColumnHeader : function(col, header){
57349         this.config[col].header = header;
57350         this.fireEvent("headerchange", this, col, header);
57351     },
57352
57353     /**
57354      * Returns the tooltip for the specified column.
57355      * @param {Number} col The column index
57356      * @return {String}
57357      */
57358     getColumnTooltip : function(col){
57359             return this.config[col].tooltip;
57360     },
57361     /**
57362      * Sets the tooltip for a column.
57363      * @param {Number} col The column index
57364      * @param {String} tooltip The new tooltip
57365      */
57366     setColumnTooltip : function(col, tooltip){
57367             this.config[col].tooltip = tooltip;
57368     },
57369
57370     /**
57371      * Returns the dataIndex for the specified column.
57372      * @param {Number} col The column index
57373      * @return {Number}
57374      */
57375     getDataIndex : function(col){
57376         return this.config[col].dataIndex;
57377     },
57378
57379     /**
57380      * Sets the dataIndex for a column.
57381      * @param {Number} col The column index
57382      * @param {Number} dataIndex The new dataIndex
57383      */
57384     setDataIndex : function(col, dataIndex){
57385         this.config[col].dataIndex = dataIndex;
57386     },
57387
57388     
57389     
57390     /**
57391      * Returns true if the cell is editable.
57392      * @param {Number} colIndex The column index
57393      * @param {Number} rowIndex The row index - this is nto actually used..?
57394      * @return {Boolean}
57395      */
57396     isCellEditable : function(colIndex, rowIndex){
57397         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57398     },
57399
57400     /**
57401      * Returns the editor defined for the cell/column.
57402      * return false or null to disable editing.
57403      * @param {Number} colIndex The column index
57404      * @param {Number} rowIndex The row index
57405      * @return {Object}
57406      */
57407     getCellEditor : function(colIndex, rowIndex){
57408         return this.config[colIndex].editor;
57409     },
57410
57411     /**
57412      * Sets if a column is editable.
57413      * @param {Number} col The column index
57414      * @param {Boolean} editable True if the column is editable
57415      */
57416     setEditable : function(col, editable){
57417         this.config[col].editable = editable;
57418     },
57419
57420
57421     /**
57422      * Returns true if the column is hidden.
57423      * @param {Number} colIndex The column index
57424      * @return {Boolean}
57425      */
57426     isHidden : function(colIndex){
57427         return this.config[colIndex].hidden;
57428     },
57429
57430
57431     /**
57432      * Returns true if the column width cannot be changed
57433      */
57434     isFixed : function(colIndex){
57435         return this.config[colIndex].fixed;
57436     },
57437
57438     /**
57439      * Returns true if the column can be resized
57440      * @return {Boolean}
57441      */
57442     isResizable : function(colIndex){
57443         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57444     },
57445     /**
57446      * Sets if a column is hidden.
57447      * @param {Number} colIndex The column index
57448      * @param {Boolean} hidden True if the column is hidden
57449      */
57450     setHidden : function(colIndex, hidden){
57451         this.config[colIndex].hidden = hidden;
57452         this.totalWidth = null;
57453         this.fireEvent("hiddenchange", this, colIndex, hidden);
57454     },
57455
57456     /**
57457      * Sets the editor for a column.
57458      * @param {Number} col The column index
57459      * @param {Object} editor The editor object
57460      */
57461     setEditor : function(col, editor){
57462         this.config[col].editor = editor;
57463     }
57464 });
57465
57466 Roo.grid.ColumnModel.defaultRenderer = function(value)
57467 {
57468     if(typeof value == "object") {
57469         return value;
57470     }
57471         if(typeof value == "string" && value.length < 1){
57472             return "&#160;";
57473         }
57474     
57475         return String.format("{0}", value);
57476 };
57477
57478 // Alias for backwards compatibility
57479 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57480 /*
57481  * Based on:
57482  * Ext JS Library 1.1.1
57483  * Copyright(c) 2006-2007, Ext JS, LLC.
57484  *
57485  * Originally Released Under LGPL - original licence link has changed is not relivant.
57486  *
57487  * Fork - LGPL
57488  * <script type="text/javascript">
57489  */
57490
57491 /**
57492  * @class Roo.grid.AbstractSelectionModel
57493  * @extends Roo.util.Observable
57494  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57495  * implemented by descendant classes.  This class should not be directly instantiated.
57496  * @constructor
57497  */
57498 Roo.grid.AbstractSelectionModel = function(){
57499     this.locked = false;
57500     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57501 };
57502
57503 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57504     /** @ignore Called by the grid automatically. Do not call directly. */
57505     init : function(grid){
57506         this.grid = grid;
57507         this.initEvents();
57508     },
57509
57510     /**
57511      * Locks the selections.
57512      */
57513     lock : function(){
57514         this.locked = true;
57515     },
57516
57517     /**
57518      * Unlocks the selections.
57519      */
57520     unlock : function(){
57521         this.locked = false;
57522     },
57523
57524     /**
57525      * Returns true if the selections are locked.
57526      * @return {Boolean}
57527      */
57528     isLocked : function(){
57529         return this.locked;
57530     }
57531 });/*
57532  * Based on:
57533  * Ext JS Library 1.1.1
57534  * Copyright(c) 2006-2007, Ext JS, LLC.
57535  *
57536  * Originally Released Under LGPL - original licence link has changed is not relivant.
57537  *
57538  * Fork - LGPL
57539  * <script type="text/javascript">
57540  */
57541 /**
57542  * @extends Roo.grid.AbstractSelectionModel
57543  * @class Roo.grid.RowSelectionModel
57544  * The default SelectionModel used by {@link Roo.grid.Grid}.
57545  * It supports multiple selections and keyboard selection/navigation. 
57546  * @constructor
57547  * @param {Object} config
57548  */
57549 Roo.grid.RowSelectionModel = function(config){
57550     Roo.apply(this, config);
57551     this.selections = new Roo.util.MixedCollection(false, function(o){
57552         return o.id;
57553     });
57554
57555     this.last = false;
57556     this.lastActive = false;
57557
57558     this.addEvents({
57559         /**
57560              * @event selectionchange
57561              * Fires when the selection changes
57562              * @param {SelectionModel} this
57563              */
57564             "selectionchange" : true,
57565         /**
57566              * @event afterselectionchange
57567              * Fires after the selection changes (eg. by key press or clicking)
57568              * @param {SelectionModel} this
57569              */
57570             "afterselectionchange" : true,
57571         /**
57572              * @event beforerowselect
57573              * Fires when a row is selected being selected, return false to cancel.
57574              * @param {SelectionModel} this
57575              * @param {Number} rowIndex The selected index
57576              * @param {Boolean} keepExisting False if other selections will be cleared
57577              */
57578             "beforerowselect" : true,
57579         /**
57580              * @event rowselect
57581              * Fires when a row is selected.
57582              * @param {SelectionModel} this
57583              * @param {Number} rowIndex The selected index
57584              * @param {Roo.data.Record} r The record
57585              */
57586             "rowselect" : true,
57587         /**
57588              * @event rowdeselect
57589              * Fires when a row is deselected.
57590              * @param {SelectionModel} this
57591              * @param {Number} rowIndex The selected index
57592              */
57593         "rowdeselect" : true
57594     });
57595     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57596     this.locked = false;
57597 };
57598
57599 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57600     /**
57601      * @cfg {Boolean} singleSelect
57602      * True to allow selection of only one row at a time (defaults to false)
57603      */
57604     singleSelect : false,
57605
57606     // private
57607     initEvents : function(){
57608
57609         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57610             this.grid.on("mousedown", this.handleMouseDown, this);
57611         }else{ // allow click to work like normal
57612             this.grid.on("rowclick", this.handleDragableRowClick, this);
57613         }
57614
57615         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57616             "up" : function(e){
57617                 if(!e.shiftKey){
57618                     this.selectPrevious(e.shiftKey);
57619                 }else if(this.last !== false && this.lastActive !== false){
57620                     var last = this.last;
57621                     this.selectRange(this.last,  this.lastActive-1);
57622                     this.grid.getView().focusRow(this.lastActive);
57623                     if(last !== false){
57624                         this.last = last;
57625                     }
57626                 }else{
57627                     this.selectFirstRow();
57628                 }
57629                 this.fireEvent("afterselectionchange", this);
57630             },
57631             "down" : function(e){
57632                 if(!e.shiftKey){
57633                     this.selectNext(e.shiftKey);
57634                 }else if(this.last !== false && this.lastActive !== false){
57635                     var last = this.last;
57636                     this.selectRange(this.last,  this.lastActive+1);
57637                     this.grid.getView().focusRow(this.lastActive);
57638                     if(last !== false){
57639                         this.last = last;
57640                     }
57641                 }else{
57642                     this.selectFirstRow();
57643                 }
57644                 this.fireEvent("afterselectionchange", this);
57645             },
57646             scope: this
57647         });
57648
57649         var view = this.grid.view;
57650         view.on("refresh", this.onRefresh, this);
57651         view.on("rowupdated", this.onRowUpdated, this);
57652         view.on("rowremoved", this.onRemove, this);
57653     },
57654
57655     // private
57656     onRefresh : function(){
57657         var ds = this.grid.dataSource, i, v = this.grid.view;
57658         var s = this.selections;
57659         s.each(function(r){
57660             if((i = ds.indexOfId(r.id)) != -1){
57661                 v.onRowSelect(i);
57662                 s.add(ds.getAt(i)); // updating the selection relate data
57663             }else{
57664                 s.remove(r);
57665             }
57666         });
57667     },
57668
57669     // private
57670     onRemove : function(v, index, r){
57671         this.selections.remove(r);
57672     },
57673
57674     // private
57675     onRowUpdated : function(v, index, r){
57676         if(this.isSelected(r)){
57677             v.onRowSelect(index);
57678         }
57679     },
57680
57681     /**
57682      * Select records.
57683      * @param {Array} records The records to select
57684      * @param {Boolean} keepExisting (optional) True to keep existing selections
57685      */
57686     selectRecords : function(records, keepExisting){
57687         if(!keepExisting){
57688             this.clearSelections();
57689         }
57690         var ds = this.grid.dataSource;
57691         for(var i = 0, len = records.length; i < len; i++){
57692             this.selectRow(ds.indexOf(records[i]), true);
57693         }
57694     },
57695
57696     /**
57697      * Gets the number of selected rows.
57698      * @return {Number}
57699      */
57700     getCount : function(){
57701         return this.selections.length;
57702     },
57703
57704     /**
57705      * Selects the first row in the grid.
57706      */
57707     selectFirstRow : function(){
57708         this.selectRow(0);
57709     },
57710
57711     /**
57712      * Select the last row.
57713      * @param {Boolean} keepExisting (optional) True to keep existing selections
57714      */
57715     selectLastRow : function(keepExisting){
57716         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57717     },
57718
57719     /**
57720      * Selects the row immediately following the last selected row.
57721      * @param {Boolean} keepExisting (optional) True to keep existing selections
57722      */
57723     selectNext : function(keepExisting){
57724         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57725             this.selectRow(this.last+1, keepExisting);
57726             this.grid.getView().focusRow(this.last);
57727         }
57728     },
57729
57730     /**
57731      * Selects the row that precedes the last selected row.
57732      * @param {Boolean} keepExisting (optional) True to keep existing selections
57733      */
57734     selectPrevious : function(keepExisting){
57735         if(this.last){
57736             this.selectRow(this.last-1, keepExisting);
57737             this.grid.getView().focusRow(this.last);
57738         }
57739     },
57740
57741     /**
57742      * Returns the selected records
57743      * @return {Array} Array of selected records
57744      */
57745     getSelections : function(){
57746         return [].concat(this.selections.items);
57747     },
57748
57749     /**
57750      * Returns the first selected record.
57751      * @return {Record}
57752      */
57753     getSelected : function(){
57754         return this.selections.itemAt(0);
57755     },
57756
57757
57758     /**
57759      * Clears all selections.
57760      */
57761     clearSelections : function(fast){
57762         if(this.locked) {
57763             return;
57764         }
57765         if(fast !== true){
57766             var ds = this.grid.dataSource;
57767             var s = this.selections;
57768             s.each(function(r){
57769                 this.deselectRow(ds.indexOfId(r.id));
57770             }, this);
57771             s.clear();
57772         }else{
57773             this.selections.clear();
57774         }
57775         this.last = false;
57776     },
57777
57778
57779     /**
57780      * Selects all rows.
57781      */
57782     selectAll : function(){
57783         if(this.locked) {
57784             return;
57785         }
57786         this.selections.clear();
57787         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57788             this.selectRow(i, true);
57789         }
57790     },
57791
57792     /**
57793      * Returns True if there is a selection.
57794      * @return {Boolean}
57795      */
57796     hasSelection : function(){
57797         return this.selections.length > 0;
57798     },
57799
57800     /**
57801      * Returns True if the specified row is selected.
57802      * @param {Number/Record} record The record or index of the record to check
57803      * @return {Boolean}
57804      */
57805     isSelected : function(index){
57806         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57807         return (r && this.selections.key(r.id) ? true : false);
57808     },
57809
57810     /**
57811      * Returns True if the specified record id is selected.
57812      * @param {String} id The id of record to check
57813      * @return {Boolean}
57814      */
57815     isIdSelected : function(id){
57816         return (this.selections.key(id) ? true : false);
57817     },
57818
57819     // private
57820     handleMouseDown : function(e, t){
57821         var view = this.grid.getView(), rowIndex;
57822         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57823             return;
57824         };
57825         if(e.shiftKey && this.last !== false){
57826             var last = this.last;
57827             this.selectRange(last, rowIndex, e.ctrlKey);
57828             this.last = last; // reset the last
57829             view.focusRow(rowIndex);
57830         }else{
57831             var isSelected = this.isSelected(rowIndex);
57832             if(e.button !== 0 && isSelected){
57833                 view.focusRow(rowIndex);
57834             }else if(e.ctrlKey && isSelected){
57835                 this.deselectRow(rowIndex);
57836             }else if(!isSelected){
57837                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57838                 view.focusRow(rowIndex);
57839             }
57840         }
57841         this.fireEvent("afterselectionchange", this);
57842     },
57843     // private
57844     handleDragableRowClick :  function(grid, rowIndex, e) 
57845     {
57846         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57847             this.selectRow(rowIndex, false);
57848             grid.view.focusRow(rowIndex);
57849              this.fireEvent("afterselectionchange", this);
57850         }
57851     },
57852     
57853     /**
57854      * Selects multiple rows.
57855      * @param {Array} rows Array of the indexes of the row to select
57856      * @param {Boolean} keepExisting (optional) True to keep existing selections
57857      */
57858     selectRows : function(rows, keepExisting){
57859         if(!keepExisting){
57860             this.clearSelections();
57861         }
57862         for(var i = 0, len = rows.length; i < len; i++){
57863             this.selectRow(rows[i], true);
57864         }
57865     },
57866
57867     /**
57868      * Selects a range of rows. All rows in between startRow and endRow are also selected.
57869      * @param {Number} startRow The index of the first row in the range
57870      * @param {Number} endRow The index of the last row in the range
57871      * @param {Boolean} keepExisting (optional) True to retain existing selections
57872      */
57873     selectRange : function(startRow, endRow, keepExisting){
57874         if(this.locked) {
57875             return;
57876         }
57877         if(!keepExisting){
57878             this.clearSelections();
57879         }
57880         if(startRow <= endRow){
57881             for(var i = startRow; i <= endRow; i++){
57882                 this.selectRow(i, true);
57883             }
57884         }else{
57885             for(var i = startRow; i >= endRow; i--){
57886                 this.selectRow(i, true);
57887             }
57888         }
57889     },
57890
57891     /**
57892      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
57893      * @param {Number} startRow The index of the first row in the range
57894      * @param {Number} endRow The index of the last row in the range
57895      */
57896     deselectRange : function(startRow, endRow, preventViewNotify){
57897         if(this.locked) {
57898             return;
57899         }
57900         for(var i = startRow; i <= endRow; i++){
57901             this.deselectRow(i, preventViewNotify);
57902         }
57903     },
57904
57905     /**
57906      * Selects a row.
57907      * @param {Number} row The index of the row to select
57908      * @param {Boolean} keepExisting (optional) True to keep existing selections
57909      */
57910     selectRow : function(index, keepExisting, preventViewNotify){
57911         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
57912             return;
57913         }
57914         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
57915             if(!keepExisting || this.singleSelect){
57916                 this.clearSelections();
57917             }
57918             var r = this.grid.dataSource.getAt(index);
57919             this.selections.add(r);
57920             this.last = this.lastActive = index;
57921             if(!preventViewNotify){
57922                 this.grid.getView().onRowSelect(index);
57923             }
57924             this.fireEvent("rowselect", this, index, r);
57925             this.fireEvent("selectionchange", this);
57926         }
57927     },
57928
57929     /**
57930      * Deselects a row.
57931      * @param {Number} row The index of the row to deselect
57932      */
57933     deselectRow : function(index, preventViewNotify){
57934         if(this.locked) {
57935             return;
57936         }
57937         if(this.last == index){
57938             this.last = false;
57939         }
57940         if(this.lastActive == index){
57941             this.lastActive = false;
57942         }
57943         var r = this.grid.dataSource.getAt(index);
57944         this.selections.remove(r);
57945         if(!preventViewNotify){
57946             this.grid.getView().onRowDeselect(index);
57947         }
57948         this.fireEvent("rowdeselect", this, index);
57949         this.fireEvent("selectionchange", this);
57950     },
57951
57952     // private
57953     restoreLast : function(){
57954         if(this._last){
57955             this.last = this._last;
57956         }
57957     },
57958
57959     // private
57960     acceptsNav : function(row, col, cm){
57961         return !cm.isHidden(col) && cm.isCellEditable(col, row);
57962     },
57963
57964     // private
57965     onEditorKey : function(field, e){
57966         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
57967         if(k == e.TAB){
57968             e.stopEvent();
57969             ed.completeEdit();
57970             if(e.shiftKey){
57971                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
57972             }else{
57973                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
57974             }
57975         }else if(k == e.ENTER && !e.ctrlKey){
57976             e.stopEvent();
57977             ed.completeEdit();
57978             if(e.shiftKey){
57979                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
57980             }else{
57981                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
57982             }
57983         }else if(k == e.ESC){
57984             ed.cancelEdit();
57985         }
57986         if(newCell){
57987             g.startEditing(newCell[0], newCell[1]);
57988         }
57989     }
57990 });/*
57991  * Based on:
57992  * Ext JS Library 1.1.1
57993  * Copyright(c) 2006-2007, Ext JS, LLC.
57994  *
57995  * Originally Released Under LGPL - original licence link has changed is not relivant.
57996  *
57997  * Fork - LGPL
57998  * <script type="text/javascript">
57999  */
58000 /**
58001  * @class Roo.grid.CellSelectionModel
58002  * @extends Roo.grid.AbstractSelectionModel
58003  * This class provides the basic implementation for cell selection in a grid.
58004  * @constructor
58005  * @param {Object} config The object containing the configuration of this model.
58006  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
58007  */
58008 Roo.grid.CellSelectionModel = function(config){
58009     Roo.apply(this, config);
58010
58011     this.selection = null;
58012
58013     this.addEvents({
58014         /**
58015              * @event beforerowselect
58016              * Fires before a cell is selected.
58017              * @param {SelectionModel} this
58018              * @param {Number} rowIndex The selected row index
58019              * @param {Number} colIndex The selected cell index
58020              */
58021             "beforecellselect" : true,
58022         /**
58023              * @event cellselect
58024              * Fires when a cell is selected.
58025              * @param {SelectionModel} this
58026              * @param {Number} rowIndex The selected row index
58027              * @param {Number} colIndex The selected cell index
58028              */
58029             "cellselect" : true,
58030         /**
58031              * @event selectionchange
58032              * Fires when the active selection changes.
58033              * @param {SelectionModel} this
58034              * @param {Object} selection null for no selection or an object (o) with two properties
58035                 <ul>
58036                 <li>o.record: the record object for the row the selection is in</li>
58037                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58038                 </ul>
58039              */
58040             "selectionchange" : true,
58041         /**
58042              * @event tabend
58043              * Fires when the tab (or enter) was pressed on the last editable cell
58044              * You can use this to trigger add new row.
58045              * @param {SelectionModel} this
58046              */
58047             "tabend" : true,
58048          /**
58049              * @event beforeeditnext
58050              * Fires before the next editable sell is made active
58051              * You can use this to skip to another cell or fire the tabend
58052              *    if you set cell to false
58053              * @param {Object} eventdata object : { cell : [ row, col ] } 
58054              */
58055             "beforeeditnext" : true
58056     });
58057     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58058 };
58059
58060 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58061     
58062     enter_is_tab: false,
58063
58064     /** @ignore */
58065     initEvents : function(){
58066         this.grid.on("mousedown", this.handleMouseDown, this);
58067         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58068         var view = this.grid.view;
58069         view.on("refresh", this.onViewChange, this);
58070         view.on("rowupdated", this.onRowUpdated, this);
58071         view.on("beforerowremoved", this.clearSelections, this);
58072         view.on("beforerowsinserted", this.clearSelections, this);
58073         if(this.grid.isEditor){
58074             this.grid.on("beforeedit", this.beforeEdit,  this);
58075         }
58076     },
58077
58078         //private
58079     beforeEdit : function(e){
58080         this.select(e.row, e.column, false, true, e.record);
58081     },
58082
58083         //private
58084     onRowUpdated : function(v, index, r){
58085         if(this.selection && this.selection.record == r){
58086             v.onCellSelect(index, this.selection.cell[1]);
58087         }
58088     },
58089
58090         //private
58091     onViewChange : function(){
58092         this.clearSelections(true);
58093     },
58094
58095         /**
58096          * Returns the currently selected cell,.
58097          * @return {Array} The selected cell (row, column) or null if none selected.
58098          */
58099     getSelectedCell : function(){
58100         return this.selection ? this.selection.cell : null;
58101     },
58102
58103     /**
58104      * Clears all selections.
58105      * @param {Boolean} true to prevent the gridview from being notified about the change.
58106      */
58107     clearSelections : function(preventNotify){
58108         var s = this.selection;
58109         if(s){
58110             if(preventNotify !== true){
58111                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58112             }
58113             this.selection = null;
58114             this.fireEvent("selectionchange", this, null);
58115         }
58116     },
58117
58118     /**
58119      * Returns true if there is a selection.
58120      * @return {Boolean}
58121      */
58122     hasSelection : function(){
58123         return this.selection ? true : false;
58124     },
58125
58126     /** @ignore */
58127     handleMouseDown : function(e, t){
58128         var v = this.grid.getView();
58129         if(this.isLocked()){
58130             return;
58131         };
58132         var row = v.findRowIndex(t);
58133         var cell = v.findCellIndex(t);
58134         if(row !== false && cell !== false){
58135             this.select(row, cell);
58136         }
58137     },
58138
58139     /**
58140      * Selects a cell.
58141      * @param {Number} rowIndex
58142      * @param {Number} collIndex
58143      */
58144     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58145         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58146             this.clearSelections();
58147             r = r || this.grid.dataSource.getAt(rowIndex);
58148             this.selection = {
58149                 record : r,
58150                 cell : [rowIndex, colIndex]
58151             };
58152             if(!preventViewNotify){
58153                 var v = this.grid.getView();
58154                 v.onCellSelect(rowIndex, colIndex);
58155                 if(preventFocus !== true){
58156                     v.focusCell(rowIndex, colIndex);
58157                 }
58158             }
58159             this.fireEvent("cellselect", this, rowIndex, colIndex);
58160             this.fireEvent("selectionchange", this, this.selection);
58161         }
58162     },
58163
58164         //private
58165     isSelectable : function(rowIndex, colIndex, cm){
58166         return !cm.isHidden(colIndex);
58167     },
58168
58169     /** @ignore */
58170     handleKeyDown : function(e){
58171         //Roo.log('Cell Sel Model handleKeyDown');
58172         if(!e.isNavKeyPress()){
58173             return;
58174         }
58175         var g = this.grid, s = this.selection;
58176         if(!s){
58177             e.stopEvent();
58178             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58179             if(cell){
58180                 this.select(cell[0], cell[1]);
58181             }
58182             return;
58183         }
58184         var sm = this;
58185         var walk = function(row, col, step){
58186             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58187         };
58188         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58189         var newCell;
58190
58191       
58192
58193         switch(k){
58194             case e.TAB:
58195                 // handled by onEditorKey
58196                 if (g.isEditor && g.editing) {
58197                     return;
58198                 }
58199                 if(e.shiftKey) {
58200                     newCell = walk(r, c-1, -1);
58201                 } else {
58202                     newCell = walk(r, c+1, 1);
58203                 }
58204                 break;
58205             
58206             case e.DOWN:
58207                newCell = walk(r+1, c, 1);
58208                 break;
58209             
58210             case e.UP:
58211                 newCell = walk(r-1, c, -1);
58212                 break;
58213             
58214             case e.RIGHT:
58215                 newCell = walk(r, c+1, 1);
58216                 break;
58217             
58218             case e.LEFT:
58219                 newCell = walk(r, c-1, -1);
58220                 break;
58221             
58222             case e.ENTER:
58223                 
58224                 if(g.isEditor && !g.editing){
58225                    g.startEditing(r, c);
58226                    e.stopEvent();
58227                    return;
58228                 }
58229                 
58230                 
58231              break;
58232         };
58233         if(newCell){
58234             this.select(newCell[0], newCell[1]);
58235             e.stopEvent();
58236             
58237         }
58238     },
58239
58240     acceptsNav : function(row, col, cm){
58241         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58242     },
58243     /**
58244      * Selects a cell.
58245      * @param {Number} field (not used) - as it's normally used as a listener
58246      * @param {Number} e - event - fake it by using
58247      *
58248      * var e = Roo.EventObjectImpl.prototype;
58249      * e.keyCode = e.TAB
58250      *
58251      * 
58252      */
58253     onEditorKey : function(field, e){
58254         
58255         var k = e.getKey(),
58256             newCell,
58257             g = this.grid,
58258             ed = g.activeEditor,
58259             forward = false;
58260         ///Roo.log('onEditorKey' + k);
58261         
58262         
58263         if (this.enter_is_tab && k == e.ENTER) {
58264             k = e.TAB;
58265         }
58266         
58267         if(k == e.TAB){
58268             if(e.shiftKey){
58269                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58270             }else{
58271                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58272                 forward = true;
58273             }
58274             
58275             e.stopEvent();
58276             
58277         } else if(k == e.ENTER &&  !e.ctrlKey){
58278             ed.completeEdit();
58279             e.stopEvent();
58280             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58281         
58282                 } else if(k == e.ESC){
58283             ed.cancelEdit();
58284         }
58285                 
58286         if (newCell) {
58287             var ecall = { cell : newCell, forward : forward };
58288             this.fireEvent('beforeeditnext', ecall );
58289             newCell = ecall.cell;
58290                         forward = ecall.forward;
58291         }
58292                 
58293         if(newCell){
58294             //Roo.log('next cell after edit');
58295             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58296         } else if (forward) {
58297             // tabbed past last
58298             this.fireEvent.defer(100, this, ['tabend',this]);
58299         }
58300     }
58301 });/*
58302  * Based on:
58303  * Ext JS Library 1.1.1
58304  * Copyright(c) 2006-2007, Ext JS, LLC.
58305  *
58306  * Originally Released Under LGPL - original licence link has changed is not relivant.
58307  *
58308  * Fork - LGPL
58309  * <script type="text/javascript">
58310  */
58311  
58312 /**
58313  * @class Roo.grid.EditorGrid
58314  * @extends Roo.grid.Grid
58315  * Class for creating and editable grid.
58316  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58317  * The container MUST have some type of size defined for the grid to fill. The container will be 
58318  * automatically set to position relative if it isn't already.
58319  * @param {Object} dataSource The data model to bind to
58320  * @param {Object} colModel The column model with info about this grid's columns
58321  */
58322 Roo.grid.EditorGrid = function(container, config){
58323     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58324     this.getGridEl().addClass("xedit-grid");
58325
58326     if(!this.selModel){
58327         this.selModel = new Roo.grid.CellSelectionModel();
58328     }
58329
58330     this.activeEditor = null;
58331
58332         this.addEvents({
58333             /**
58334              * @event beforeedit
58335              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58336              * <ul style="padding:5px;padding-left:16px;">
58337              * <li>grid - This grid</li>
58338              * <li>record - The record being edited</li>
58339              * <li>field - The field name being edited</li>
58340              * <li>value - The value for the field being edited.</li>
58341              * <li>row - The grid row index</li>
58342              * <li>column - The grid column index</li>
58343              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58344              * </ul>
58345              * @param {Object} e An edit event (see above for description)
58346              */
58347             "beforeedit" : true,
58348             /**
58349              * @event afteredit
58350              * Fires after a cell is edited. <br />
58351              * <ul style="padding:5px;padding-left:16px;">
58352              * <li>grid - This grid</li>
58353              * <li>record - The record being edited</li>
58354              * <li>field - The field name being edited</li>
58355              * <li>value - The value being set</li>
58356              * <li>originalValue - The original value for the field, before the edit.</li>
58357              * <li>row - The grid row index</li>
58358              * <li>column - The grid column index</li>
58359              * </ul>
58360              * @param {Object} e An edit event (see above for description)
58361              */
58362             "afteredit" : true,
58363             /**
58364              * @event validateedit
58365              * Fires after a cell is edited, but before the value is set in the record. 
58366          * You can use this to modify the value being set in the field, Return false
58367              * to cancel the change. The edit event object has the following properties <br />
58368              * <ul style="padding:5px;padding-left:16px;">
58369          * <li>editor - This editor</li>
58370              * <li>grid - This grid</li>
58371              * <li>record - The record being edited</li>
58372              * <li>field - The field name being edited</li>
58373              * <li>value - The value being set</li>
58374              * <li>originalValue - The original value for the field, before the edit.</li>
58375              * <li>row - The grid row index</li>
58376              * <li>column - The grid column index</li>
58377              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58378              * </ul>
58379              * @param {Object} e An edit event (see above for description)
58380              */
58381             "validateedit" : true
58382         });
58383     this.on("bodyscroll", this.stopEditing,  this);
58384     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58385 };
58386
58387 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58388     /**
58389      * @cfg {Number} clicksToEdit
58390      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58391      */
58392     clicksToEdit: 2,
58393
58394     // private
58395     isEditor : true,
58396     // private
58397     trackMouseOver: false, // causes very odd FF errors
58398
58399     onCellDblClick : function(g, row, col){
58400         this.startEditing(row, col);
58401     },
58402
58403     onEditComplete : function(ed, value, startValue){
58404         this.editing = false;
58405         this.activeEditor = null;
58406         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58407         var r = ed.record;
58408         var field = this.colModel.getDataIndex(ed.col);
58409         var e = {
58410             grid: this,
58411             record: r,
58412             field: field,
58413             originalValue: startValue,
58414             value: value,
58415             row: ed.row,
58416             column: ed.col,
58417             cancel:false,
58418             editor: ed
58419         };
58420         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58421         cell.show();
58422           
58423         if(String(value) !== String(startValue)){
58424             
58425             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58426                 r.set(field, e.value);
58427                 // if we are dealing with a combo box..
58428                 // then we also set the 'name' colum to be the displayField
58429                 if (ed.field.displayField && ed.field.name) {
58430                     r.set(ed.field.name, ed.field.el.dom.value);
58431                 }
58432                 
58433                 delete e.cancel; //?? why!!!
58434                 this.fireEvent("afteredit", e);
58435             }
58436         } else {
58437             this.fireEvent("afteredit", e); // always fire it!
58438         }
58439         this.view.focusCell(ed.row, ed.col);
58440     },
58441
58442     /**
58443      * Starts editing the specified for the specified row/column
58444      * @param {Number} rowIndex
58445      * @param {Number} colIndex
58446      */
58447     startEditing : function(row, col){
58448         this.stopEditing();
58449         if(this.colModel.isCellEditable(col, row)){
58450             this.view.ensureVisible(row, col, true);
58451           
58452             var r = this.dataSource.getAt(row);
58453             var field = this.colModel.getDataIndex(col);
58454             var cell = Roo.get(this.view.getCell(row,col));
58455             var e = {
58456                 grid: this,
58457                 record: r,
58458                 field: field,
58459                 value: r.data[field],
58460                 row: row,
58461                 column: col,
58462                 cancel:false 
58463             };
58464             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58465                 this.editing = true;
58466                 var ed = this.colModel.getCellEditor(col, row);
58467                 
58468                 if (!ed) {
58469                     return;
58470                 }
58471                 if(!ed.rendered){
58472                     ed.render(ed.parentEl || document.body);
58473                 }
58474                 ed.field.reset();
58475                
58476                 cell.hide();
58477                 
58478                 (function(){ // complex but required for focus issues in safari, ie and opera
58479                     ed.row = row;
58480                     ed.col = col;
58481                     ed.record = r;
58482                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58483                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58484                     this.activeEditor = ed;
58485                     var v = r.data[field];
58486                     ed.startEdit(this.view.getCell(row, col), v);
58487                     // combo's with 'displayField and name set
58488                     if (ed.field.displayField && ed.field.name) {
58489                         ed.field.el.dom.value = r.data[ed.field.name];
58490                     }
58491                     
58492                     
58493                 }).defer(50, this);
58494             }
58495         }
58496     },
58497         
58498     /**
58499      * Stops any active editing
58500      */
58501     stopEditing : function(){
58502         if(this.activeEditor){
58503             this.activeEditor.completeEdit();
58504         }
58505         this.activeEditor = null;
58506     },
58507         
58508          /**
58509      * Called to get grid's drag proxy text, by default returns this.ddText.
58510      * @return {String}
58511      */
58512     getDragDropText : function(){
58513         var count = this.selModel.getSelectedCell() ? 1 : 0;
58514         return String.format(this.ddText, count, count == 1 ? '' : 's');
58515     }
58516         
58517 });/*
58518  * Based on:
58519  * Ext JS Library 1.1.1
58520  * Copyright(c) 2006-2007, Ext JS, LLC.
58521  *
58522  * Originally Released Under LGPL - original licence link has changed is not relivant.
58523  *
58524  * Fork - LGPL
58525  * <script type="text/javascript">
58526  */
58527
58528 // private - not really -- you end up using it !
58529 // This is a support class used internally by the Grid components
58530
58531 /**
58532  * @class Roo.grid.GridEditor
58533  * @extends Roo.Editor
58534  * Class for creating and editable grid elements.
58535  * @param {Object} config any settings (must include field)
58536  */
58537 Roo.grid.GridEditor = function(field, config){
58538     if (!config && field.field) {
58539         config = field;
58540         field = Roo.factory(config.field, Roo.form);
58541     }
58542     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58543     field.monitorTab = false;
58544 };
58545
58546 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58547     
58548     /**
58549      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58550      */
58551     
58552     alignment: "tl-tl",
58553     autoSize: "width",
58554     hideEl : false,
58555     cls: "x-small-editor x-grid-editor",
58556     shim:false,
58557     shadow:"frame"
58558 });/*
58559  * Based on:
58560  * Ext JS Library 1.1.1
58561  * Copyright(c) 2006-2007, Ext JS, LLC.
58562  *
58563  * Originally Released Under LGPL - original licence link has changed is not relivant.
58564  *
58565  * Fork - LGPL
58566  * <script type="text/javascript">
58567  */
58568   
58569
58570   
58571 Roo.grid.PropertyRecord = Roo.data.Record.create([
58572     {name:'name',type:'string'},  'value'
58573 ]);
58574
58575
58576 Roo.grid.PropertyStore = function(grid, source){
58577     this.grid = grid;
58578     this.store = new Roo.data.Store({
58579         recordType : Roo.grid.PropertyRecord
58580     });
58581     this.store.on('update', this.onUpdate,  this);
58582     if(source){
58583         this.setSource(source);
58584     }
58585     Roo.grid.PropertyStore.superclass.constructor.call(this);
58586 };
58587
58588
58589
58590 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58591     setSource : function(o){
58592         this.source = o;
58593         this.store.removeAll();
58594         var data = [];
58595         for(var k in o){
58596             if(this.isEditableValue(o[k])){
58597                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58598             }
58599         }
58600         this.store.loadRecords({records: data}, {}, true);
58601     },
58602
58603     onUpdate : function(ds, record, type){
58604         if(type == Roo.data.Record.EDIT){
58605             var v = record.data['value'];
58606             var oldValue = record.modified['value'];
58607             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58608                 this.source[record.id] = v;
58609                 record.commit();
58610                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58611             }else{
58612                 record.reject();
58613             }
58614         }
58615     },
58616
58617     getProperty : function(row){
58618        return this.store.getAt(row);
58619     },
58620
58621     isEditableValue: function(val){
58622         if(val && val instanceof Date){
58623             return true;
58624         }else if(typeof val == 'object' || typeof val == 'function'){
58625             return false;
58626         }
58627         return true;
58628     },
58629
58630     setValue : function(prop, value){
58631         this.source[prop] = value;
58632         this.store.getById(prop).set('value', value);
58633     },
58634
58635     getSource : function(){
58636         return this.source;
58637     }
58638 });
58639
58640 Roo.grid.PropertyColumnModel = function(grid, store){
58641     this.grid = grid;
58642     var g = Roo.grid;
58643     g.PropertyColumnModel.superclass.constructor.call(this, [
58644         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58645         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58646     ]);
58647     this.store = store;
58648     this.bselect = Roo.DomHelper.append(document.body, {
58649         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58650             {tag: 'option', value: 'true', html: 'true'},
58651             {tag: 'option', value: 'false', html: 'false'}
58652         ]
58653     });
58654     Roo.id(this.bselect);
58655     var f = Roo.form;
58656     this.editors = {
58657         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58658         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58659         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58660         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58661         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58662     };
58663     this.renderCellDelegate = this.renderCell.createDelegate(this);
58664     this.renderPropDelegate = this.renderProp.createDelegate(this);
58665 };
58666
58667 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58668     
58669     
58670     nameText : 'Name',
58671     valueText : 'Value',
58672     
58673     dateFormat : 'm/j/Y',
58674     
58675     
58676     renderDate : function(dateVal){
58677         return dateVal.dateFormat(this.dateFormat);
58678     },
58679
58680     renderBool : function(bVal){
58681         return bVal ? 'true' : 'false';
58682     },
58683
58684     isCellEditable : function(colIndex, rowIndex){
58685         return colIndex == 1;
58686     },
58687
58688     getRenderer : function(col){
58689         return col == 1 ?
58690             this.renderCellDelegate : this.renderPropDelegate;
58691     },
58692
58693     renderProp : function(v){
58694         return this.getPropertyName(v);
58695     },
58696
58697     renderCell : function(val){
58698         var rv = val;
58699         if(val instanceof Date){
58700             rv = this.renderDate(val);
58701         }else if(typeof val == 'boolean'){
58702             rv = this.renderBool(val);
58703         }
58704         return Roo.util.Format.htmlEncode(rv);
58705     },
58706
58707     getPropertyName : function(name){
58708         var pn = this.grid.propertyNames;
58709         return pn && pn[name] ? pn[name] : name;
58710     },
58711
58712     getCellEditor : function(colIndex, rowIndex){
58713         var p = this.store.getProperty(rowIndex);
58714         var n = p.data['name'], val = p.data['value'];
58715         
58716         if(typeof(this.grid.customEditors[n]) == 'string'){
58717             return this.editors[this.grid.customEditors[n]];
58718         }
58719         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58720             return this.grid.customEditors[n];
58721         }
58722         if(val instanceof Date){
58723             return this.editors['date'];
58724         }else if(typeof val == 'number'){
58725             return this.editors['number'];
58726         }else if(typeof val == 'boolean'){
58727             return this.editors['boolean'];
58728         }else{
58729             return this.editors['string'];
58730         }
58731     }
58732 });
58733
58734 /**
58735  * @class Roo.grid.PropertyGrid
58736  * @extends Roo.grid.EditorGrid
58737  * This class represents the  interface of a component based property grid control.
58738  * <br><br>Usage:<pre><code>
58739  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58740       
58741  });
58742  // set any options
58743  grid.render();
58744  * </code></pre>
58745   
58746  * @constructor
58747  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58748  * The container MUST have some type of size defined for the grid to fill. The container will be
58749  * automatically set to position relative if it isn't already.
58750  * @param {Object} config A config object that sets properties on this grid.
58751  */
58752 Roo.grid.PropertyGrid = function(container, config){
58753     config = config || {};
58754     var store = new Roo.grid.PropertyStore(this);
58755     this.store = store;
58756     var cm = new Roo.grid.PropertyColumnModel(this, store);
58757     store.store.sort('name', 'ASC');
58758     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58759         ds: store.store,
58760         cm: cm,
58761         enableColLock:false,
58762         enableColumnMove:false,
58763         stripeRows:false,
58764         trackMouseOver: false,
58765         clicksToEdit:1
58766     }, config));
58767     this.getGridEl().addClass('x-props-grid');
58768     this.lastEditRow = null;
58769     this.on('columnresize', this.onColumnResize, this);
58770     this.addEvents({
58771          /**
58772              * @event beforepropertychange
58773              * Fires before a property changes (return false to stop?)
58774              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58775              * @param {String} id Record Id
58776              * @param {String} newval New Value
58777          * @param {String} oldval Old Value
58778              */
58779         "beforepropertychange": true,
58780         /**
58781              * @event propertychange
58782              * Fires after a property changes
58783              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58784              * @param {String} id Record Id
58785              * @param {String} newval New Value
58786          * @param {String} oldval Old Value
58787              */
58788         "propertychange": true
58789     });
58790     this.customEditors = this.customEditors || {};
58791 };
58792 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58793     
58794      /**
58795      * @cfg {Object} customEditors map of colnames=> custom editors.
58796      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58797      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58798      * false disables editing of the field.
58799          */
58800     
58801       /**
58802      * @cfg {Object} propertyNames map of property Names to their displayed value
58803          */
58804     
58805     render : function(){
58806         Roo.grid.PropertyGrid.superclass.render.call(this);
58807         this.autoSize.defer(100, this);
58808     },
58809
58810     autoSize : function(){
58811         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58812         if(this.view){
58813             this.view.fitColumns();
58814         }
58815     },
58816
58817     onColumnResize : function(){
58818         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58819         this.autoSize();
58820     },
58821     /**
58822      * Sets the data for the Grid
58823      * accepts a Key => Value object of all the elements avaiable.
58824      * @param {Object} data  to appear in grid.
58825      */
58826     setSource : function(source){
58827         this.store.setSource(source);
58828         //this.autoSize();
58829     },
58830     /**
58831      * Gets all the data from the grid.
58832      * @return {Object} data  data stored in grid
58833      */
58834     getSource : function(){
58835         return this.store.getSource();
58836     }
58837 });/*
58838   
58839  * Licence LGPL
58840  
58841  */
58842  
58843 /**
58844  * @class Roo.grid.Calendar
58845  * @extends Roo.util.Grid
58846  * This class extends the Grid to provide a calendar widget
58847  * <br><br>Usage:<pre><code>
58848  var grid = new Roo.grid.Calendar("my-container-id", {
58849      ds: myDataStore,
58850      cm: myColModel,
58851      selModel: mySelectionModel,
58852      autoSizeColumns: true,
58853      monitorWindowResize: false,
58854      trackMouseOver: true
58855      eventstore : real data store..
58856  });
58857  // set any options
58858  grid.render();
58859   
58860   * @constructor
58861  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58862  * The container MUST have some type of size defined for the grid to fill. The container will be
58863  * automatically set to position relative if it isn't already.
58864  * @param {Object} config A config object that sets properties on this grid.
58865  */
58866 Roo.grid.Calendar = function(container, config){
58867         // initialize the container
58868         this.container = Roo.get(container);
58869         this.container.update("");
58870         this.container.setStyle("overflow", "hidden");
58871     this.container.addClass('x-grid-container');
58872
58873     this.id = this.container.id;
58874
58875     Roo.apply(this, config);
58876     // check and correct shorthanded configs
58877     
58878     var rows = [];
58879     var d =1;
58880     for (var r = 0;r < 6;r++) {
58881         
58882         rows[r]=[];
58883         for (var c =0;c < 7;c++) {
58884             rows[r][c]= '';
58885         }
58886     }
58887     if (this.eventStore) {
58888         this.eventStore= Roo.factory(this.eventStore, Roo.data);
58889         this.eventStore.on('load',this.onLoad, this);
58890         this.eventStore.on('beforeload',this.clearEvents, this);
58891          
58892     }
58893     
58894     this.dataSource = new Roo.data.Store({
58895             proxy: new Roo.data.MemoryProxy(rows),
58896             reader: new Roo.data.ArrayReader({}, [
58897                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
58898     });
58899
58900     this.dataSource.load();
58901     this.ds = this.dataSource;
58902     this.ds.xmodule = this.xmodule || false;
58903     
58904     
58905     var cellRender = function(v,x,r)
58906     {
58907         return String.format(
58908             '<div class="fc-day  fc-widget-content"><div>' +
58909                 '<div class="fc-event-container"></div>' +
58910                 '<div class="fc-day-number">{0}</div>'+
58911                 
58912                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
58913             '</div></div>', v);
58914     
58915     }
58916     
58917     
58918     this.colModel = new Roo.grid.ColumnModel( [
58919         {
58920             xtype: 'ColumnModel',
58921             xns: Roo.grid,
58922             dataIndex : 'weekday0',
58923             header : 'Sunday',
58924             renderer : cellRender
58925         },
58926         {
58927             xtype: 'ColumnModel',
58928             xns: Roo.grid,
58929             dataIndex : 'weekday1',
58930             header : 'Monday',
58931             renderer : cellRender
58932         },
58933         {
58934             xtype: 'ColumnModel',
58935             xns: Roo.grid,
58936             dataIndex : 'weekday2',
58937             header : 'Tuesday',
58938             renderer : cellRender
58939         },
58940         {
58941             xtype: 'ColumnModel',
58942             xns: Roo.grid,
58943             dataIndex : 'weekday3',
58944             header : 'Wednesday',
58945             renderer : cellRender
58946         },
58947         {
58948             xtype: 'ColumnModel',
58949             xns: Roo.grid,
58950             dataIndex : 'weekday4',
58951             header : 'Thursday',
58952             renderer : cellRender
58953         },
58954         {
58955             xtype: 'ColumnModel',
58956             xns: Roo.grid,
58957             dataIndex : 'weekday5',
58958             header : 'Friday',
58959             renderer : cellRender
58960         },
58961         {
58962             xtype: 'ColumnModel',
58963             xns: Roo.grid,
58964             dataIndex : 'weekday6',
58965             header : 'Saturday',
58966             renderer : cellRender
58967         }
58968     ]);
58969     this.cm = this.colModel;
58970     this.cm.xmodule = this.xmodule || false;
58971  
58972         
58973           
58974     //this.selModel = new Roo.grid.CellSelectionModel();
58975     //this.sm = this.selModel;
58976     //this.selModel.init(this);
58977     
58978     
58979     if(this.width){
58980         this.container.setWidth(this.width);
58981     }
58982
58983     if(this.height){
58984         this.container.setHeight(this.height);
58985     }
58986     /** @private */
58987         this.addEvents({
58988         // raw events
58989         /**
58990          * @event click
58991          * The raw click event for the entire grid.
58992          * @param {Roo.EventObject} e
58993          */
58994         "click" : true,
58995         /**
58996          * @event dblclick
58997          * The raw dblclick event for the entire grid.
58998          * @param {Roo.EventObject} e
58999          */
59000         "dblclick" : true,
59001         /**
59002          * @event contextmenu
59003          * The raw contextmenu event for the entire grid.
59004          * @param {Roo.EventObject} e
59005          */
59006         "contextmenu" : true,
59007         /**
59008          * @event mousedown
59009          * The raw mousedown event for the entire grid.
59010          * @param {Roo.EventObject} e
59011          */
59012         "mousedown" : true,
59013         /**
59014          * @event mouseup
59015          * The raw mouseup event for the entire grid.
59016          * @param {Roo.EventObject} e
59017          */
59018         "mouseup" : true,
59019         /**
59020          * @event mouseover
59021          * The raw mouseover event for the entire grid.
59022          * @param {Roo.EventObject} e
59023          */
59024         "mouseover" : true,
59025         /**
59026          * @event mouseout
59027          * The raw mouseout event for the entire grid.
59028          * @param {Roo.EventObject} e
59029          */
59030         "mouseout" : true,
59031         /**
59032          * @event keypress
59033          * The raw keypress event for the entire grid.
59034          * @param {Roo.EventObject} e
59035          */
59036         "keypress" : true,
59037         /**
59038          * @event keydown
59039          * The raw keydown event for the entire grid.
59040          * @param {Roo.EventObject} e
59041          */
59042         "keydown" : true,
59043
59044         // custom events
59045
59046         /**
59047          * @event cellclick
59048          * Fires when a cell is clicked
59049          * @param {Grid} this
59050          * @param {Number} rowIndex
59051          * @param {Number} columnIndex
59052          * @param {Roo.EventObject} e
59053          */
59054         "cellclick" : true,
59055         /**
59056          * @event celldblclick
59057          * Fires when a cell is double clicked
59058          * @param {Grid} this
59059          * @param {Number} rowIndex
59060          * @param {Number} columnIndex
59061          * @param {Roo.EventObject} e
59062          */
59063         "celldblclick" : true,
59064         /**
59065          * @event rowclick
59066          * Fires when a row is clicked
59067          * @param {Grid} this
59068          * @param {Number} rowIndex
59069          * @param {Roo.EventObject} e
59070          */
59071         "rowclick" : true,
59072         /**
59073          * @event rowdblclick
59074          * Fires when a row is double clicked
59075          * @param {Grid} this
59076          * @param {Number} rowIndex
59077          * @param {Roo.EventObject} e
59078          */
59079         "rowdblclick" : true,
59080         /**
59081          * @event headerclick
59082          * Fires when a header is clicked
59083          * @param {Grid} this
59084          * @param {Number} columnIndex
59085          * @param {Roo.EventObject} e
59086          */
59087         "headerclick" : true,
59088         /**
59089          * @event headerdblclick
59090          * Fires when a header cell is double clicked
59091          * @param {Grid} this
59092          * @param {Number} columnIndex
59093          * @param {Roo.EventObject} e
59094          */
59095         "headerdblclick" : true,
59096         /**
59097          * @event rowcontextmenu
59098          * Fires when a row is right clicked
59099          * @param {Grid} this
59100          * @param {Number} rowIndex
59101          * @param {Roo.EventObject} e
59102          */
59103         "rowcontextmenu" : true,
59104         /**
59105          * @event cellcontextmenu
59106          * Fires when a cell is right clicked
59107          * @param {Grid} this
59108          * @param {Number} rowIndex
59109          * @param {Number} cellIndex
59110          * @param {Roo.EventObject} e
59111          */
59112          "cellcontextmenu" : true,
59113         /**
59114          * @event headercontextmenu
59115          * Fires when a header is right clicked
59116          * @param {Grid} this
59117          * @param {Number} columnIndex
59118          * @param {Roo.EventObject} e
59119          */
59120         "headercontextmenu" : true,
59121         /**
59122          * @event bodyscroll
59123          * Fires when the body element is scrolled
59124          * @param {Number} scrollLeft
59125          * @param {Number} scrollTop
59126          */
59127         "bodyscroll" : true,
59128         /**
59129          * @event columnresize
59130          * Fires when the user resizes a column
59131          * @param {Number} columnIndex
59132          * @param {Number} newSize
59133          */
59134         "columnresize" : true,
59135         /**
59136          * @event columnmove
59137          * Fires when the user moves a column
59138          * @param {Number} oldIndex
59139          * @param {Number} newIndex
59140          */
59141         "columnmove" : true,
59142         /**
59143          * @event startdrag
59144          * Fires when row(s) start being dragged
59145          * @param {Grid} this
59146          * @param {Roo.GridDD} dd The drag drop object
59147          * @param {event} e The raw browser event
59148          */
59149         "startdrag" : true,
59150         /**
59151          * @event enddrag
59152          * Fires when a drag operation is complete
59153          * @param {Grid} this
59154          * @param {Roo.GridDD} dd The drag drop object
59155          * @param {event} e The raw browser event
59156          */
59157         "enddrag" : true,
59158         /**
59159          * @event dragdrop
59160          * Fires when dragged row(s) are dropped on a valid DD target
59161          * @param {Grid} this
59162          * @param {Roo.GridDD} dd The drag drop object
59163          * @param {String} targetId The target drag drop object
59164          * @param {event} e The raw browser event
59165          */
59166         "dragdrop" : true,
59167         /**
59168          * @event dragover
59169          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59170          * @param {Grid} this
59171          * @param {Roo.GridDD} dd The drag drop object
59172          * @param {String} targetId The target drag drop object
59173          * @param {event} e The raw browser event
59174          */
59175         "dragover" : true,
59176         /**
59177          * @event dragenter
59178          *  Fires when the dragged row(s) first cross another DD target while being dragged
59179          * @param {Grid} this
59180          * @param {Roo.GridDD} dd The drag drop object
59181          * @param {String} targetId The target drag drop object
59182          * @param {event} e The raw browser event
59183          */
59184         "dragenter" : true,
59185         /**
59186          * @event dragout
59187          * Fires when the dragged row(s) leave another DD target while being dragged
59188          * @param {Grid} this
59189          * @param {Roo.GridDD} dd The drag drop object
59190          * @param {String} targetId The target drag drop object
59191          * @param {event} e The raw browser event
59192          */
59193         "dragout" : true,
59194         /**
59195          * @event rowclass
59196          * Fires when a row is rendered, so you can change add a style to it.
59197          * @param {GridView} gridview   The grid view
59198          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59199          */
59200         'rowclass' : true,
59201
59202         /**
59203          * @event render
59204          * Fires when the grid is rendered
59205          * @param {Grid} grid
59206          */
59207         'render' : true,
59208             /**
59209              * @event select
59210              * Fires when a date is selected
59211              * @param {DatePicker} this
59212              * @param {Date} date The selected date
59213              */
59214         'select': true,
59215         /**
59216              * @event monthchange
59217              * Fires when the displayed month changes 
59218              * @param {DatePicker} this
59219              * @param {Date} date The selected month
59220              */
59221         'monthchange': true,
59222         /**
59223              * @event evententer
59224              * Fires when mouse over an event
59225              * @param {Calendar} this
59226              * @param {event} Event
59227              */
59228         'evententer': true,
59229         /**
59230              * @event eventleave
59231              * Fires when the mouse leaves an
59232              * @param {Calendar} this
59233              * @param {event}
59234              */
59235         'eventleave': true,
59236         /**
59237              * @event eventclick
59238              * Fires when the mouse click an
59239              * @param {Calendar} this
59240              * @param {event}
59241              */
59242         'eventclick': true,
59243         /**
59244              * @event eventrender
59245              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59246              * @param {Calendar} this
59247              * @param {data} data to be modified
59248              */
59249         'eventrender': true
59250         
59251     });
59252
59253     Roo.grid.Grid.superclass.constructor.call(this);
59254     this.on('render', function() {
59255         this.view.el.addClass('x-grid-cal'); 
59256         
59257         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59258
59259     },this);
59260     
59261     if (!Roo.grid.Calendar.style) {
59262         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59263             
59264             
59265             '.x-grid-cal .x-grid-col' :  {
59266                 height: 'auto !important',
59267                 'vertical-align': 'top'
59268             },
59269             '.x-grid-cal  .fc-event-hori' : {
59270                 height: '14px'
59271             }
59272              
59273             
59274         }, Roo.id());
59275     }
59276
59277     
59278     
59279 };
59280 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59281     /**
59282      * @cfg {Store} eventStore The store that loads events.
59283      */
59284     eventStore : 25,
59285
59286      
59287     activeDate : false,
59288     startDay : 0,
59289     autoWidth : true,
59290     monitorWindowResize : false,
59291
59292     
59293     resizeColumns : function() {
59294         var col = (this.view.el.getWidth() / 7) - 3;
59295         // loop through cols, and setWidth
59296         for(var i =0 ; i < 7 ; i++){
59297             this.cm.setColumnWidth(i, col);
59298         }
59299     },
59300      setDate :function(date) {
59301         
59302         Roo.log('setDate?');
59303         
59304         this.resizeColumns();
59305         var vd = this.activeDate;
59306         this.activeDate = date;
59307 //        if(vd && this.el){
59308 //            var t = date.getTime();
59309 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59310 //                Roo.log('using add remove');
59311 //                
59312 //                this.fireEvent('monthchange', this, date);
59313 //                
59314 //                this.cells.removeClass("fc-state-highlight");
59315 //                this.cells.each(function(c){
59316 //                   if(c.dateValue == t){
59317 //                       c.addClass("fc-state-highlight");
59318 //                       setTimeout(function(){
59319 //                            try{c.dom.firstChild.focus();}catch(e){}
59320 //                       }, 50);
59321 //                       return false;
59322 //                   }
59323 //                   return true;
59324 //                });
59325 //                return;
59326 //            }
59327 //        }
59328         
59329         var days = date.getDaysInMonth();
59330         
59331         var firstOfMonth = date.getFirstDateOfMonth();
59332         var startingPos = firstOfMonth.getDay()-this.startDay;
59333         
59334         if(startingPos < this.startDay){
59335             startingPos += 7;
59336         }
59337         
59338         var pm = date.add(Date.MONTH, -1);
59339         var prevStart = pm.getDaysInMonth()-startingPos;
59340 //        
59341         
59342         
59343         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59344         
59345         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59346         //this.cells.addClassOnOver('fc-state-hover');
59347         
59348         var cells = this.cells.elements;
59349         var textEls = this.textNodes;
59350         
59351         //Roo.each(cells, function(cell){
59352         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59353         //});
59354         
59355         days += startingPos;
59356
59357         // convert everything to numbers so it's fast
59358         var day = 86400000;
59359         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59360         //Roo.log(d);
59361         //Roo.log(pm);
59362         //Roo.log(prevStart);
59363         
59364         var today = new Date().clearTime().getTime();
59365         var sel = date.clearTime().getTime();
59366         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59367         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59368         var ddMatch = this.disabledDatesRE;
59369         var ddText = this.disabledDatesText;
59370         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59371         var ddaysText = this.disabledDaysText;
59372         var format = this.format;
59373         
59374         var setCellClass = function(cal, cell){
59375             
59376             //Roo.log('set Cell Class');
59377             cell.title = "";
59378             var t = d.getTime();
59379             
59380             //Roo.log(d);
59381             
59382             
59383             cell.dateValue = t;
59384             if(t == today){
59385                 cell.className += " fc-today";
59386                 cell.className += " fc-state-highlight";
59387                 cell.title = cal.todayText;
59388             }
59389             if(t == sel){
59390                 // disable highlight in other month..
59391                 cell.className += " fc-state-highlight";
59392                 
59393             }
59394             // disabling
59395             if(t < min) {
59396                 //cell.className = " fc-state-disabled";
59397                 cell.title = cal.minText;
59398                 return;
59399             }
59400             if(t > max) {
59401                 //cell.className = " fc-state-disabled";
59402                 cell.title = cal.maxText;
59403                 return;
59404             }
59405             if(ddays){
59406                 if(ddays.indexOf(d.getDay()) != -1){
59407                     // cell.title = ddaysText;
59408                    // cell.className = " fc-state-disabled";
59409                 }
59410             }
59411             if(ddMatch && format){
59412                 var fvalue = d.dateFormat(format);
59413                 if(ddMatch.test(fvalue)){
59414                     cell.title = ddText.replace("%0", fvalue);
59415                    cell.className = " fc-state-disabled";
59416                 }
59417             }
59418             
59419             if (!cell.initialClassName) {
59420                 cell.initialClassName = cell.dom.className;
59421             }
59422             
59423             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59424         };
59425
59426         var i = 0;
59427         
59428         for(; i < startingPos; i++) {
59429             cells[i].dayName =  (++prevStart);
59430             Roo.log(textEls[i]);
59431             d.setDate(d.getDate()+1);
59432             
59433             //cells[i].className = "fc-past fc-other-month";
59434             setCellClass(this, cells[i]);
59435         }
59436         
59437         var intDay = 0;
59438         
59439         for(; i < days; i++){
59440             intDay = i - startingPos + 1;
59441             cells[i].dayName =  (intDay);
59442             d.setDate(d.getDate()+1);
59443             
59444             cells[i].className = ''; // "x-date-active";
59445             setCellClass(this, cells[i]);
59446         }
59447         var extraDays = 0;
59448         
59449         for(; i < 42; i++) {
59450             //textEls[i].innerHTML = (++extraDays);
59451             
59452             d.setDate(d.getDate()+1);
59453             cells[i].dayName = (++extraDays);
59454             cells[i].className = "fc-future fc-other-month";
59455             setCellClass(this, cells[i]);
59456         }
59457         
59458         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59459         
59460         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59461         
59462         // this will cause all the cells to mis
59463         var rows= [];
59464         var i =0;
59465         for (var r = 0;r < 6;r++) {
59466             for (var c =0;c < 7;c++) {
59467                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59468             }    
59469         }
59470         
59471         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59472         for(i=0;i<cells.length;i++) {
59473             
59474             this.cells.elements[i].dayName = cells[i].dayName ;
59475             this.cells.elements[i].className = cells[i].className;
59476             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59477             this.cells.elements[i].title = cells[i].title ;
59478             this.cells.elements[i].dateValue = cells[i].dateValue ;
59479         }
59480         
59481         
59482         
59483         
59484         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59485         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59486         
59487         ////if(totalRows != 6){
59488             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59489            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59490        // }
59491         
59492         this.fireEvent('monthchange', this, date);
59493         
59494         
59495     },
59496  /**
59497      * Returns the grid's SelectionModel.
59498      * @return {SelectionModel}
59499      */
59500     getSelectionModel : function(){
59501         if(!this.selModel){
59502             this.selModel = new Roo.grid.CellSelectionModel();
59503         }
59504         return this.selModel;
59505     },
59506
59507     load: function() {
59508         this.eventStore.load()
59509         
59510         
59511         
59512     },
59513     
59514     findCell : function(dt) {
59515         dt = dt.clearTime().getTime();
59516         var ret = false;
59517         this.cells.each(function(c){
59518             //Roo.log("check " +c.dateValue + '?=' + dt);
59519             if(c.dateValue == dt){
59520                 ret = c;
59521                 return false;
59522             }
59523             return true;
59524         });
59525         
59526         return ret;
59527     },
59528     
59529     findCells : function(rec) {
59530         var s = rec.data.start_dt.clone().clearTime().getTime();
59531        // Roo.log(s);
59532         var e= rec.data.end_dt.clone().clearTime().getTime();
59533        // Roo.log(e);
59534         var ret = [];
59535         this.cells.each(function(c){
59536              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59537             
59538             if(c.dateValue > e){
59539                 return ;
59540             }
59541             if(c.dateValue < s){
59542                 return ;
59543             }
59544             ret.push(c);
59545         });
59546         
59547         return ret;    
59548     },
59549     
59550     findBestRow: function(cells)
59551     {
59552         var ret = 0;
59553         
59554         for (var i =0 ; i < cells.length;i++) {
59555             ret  = Math.max(cells[i].rows || 0,ret);
59556         }
59557         return ret;
59558         
59559     },
59560     
59561     
59562     addItem : function(rec)
59563     {
59564         // look for vertical location slot in
59565         var cells = this.findCells(rec);
59566         
59567         rec.row = this.findBestRow(cells);
59568         
59569         // work out the location.
59570         
59571         var crow = false;
59572         var rows = [];
59573         for(var i =0; i < cells.length; i++) {
59574             if (!crow) {
59575                 crow = {
59576                     start : cells[i],
59577                     end :  cells[i]
59578                 };
59579                 continue;
59580             }
59581             if (crow.start.getY() == cells[i].getY()) {
59582                 // on same row.
59583                 crow.end = cells[i];
59584                 continue;
59585             }
59586             // different row.
59587             rows.push(crow);
59588             crow = {
59589                 start: cells[i],
59590                 end : cells[i]
59591             };
59592             
59593         }
59594         
59595         rows.push(crow);
59596         rec.els = [];
59597         rec.rows = rows;
59598         rec.cells = cells;
59599         for (var i = 0; i < cells.length;i++) {
59600             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59601             
59602         }
59603         
59604         
59605     },
59606     
59607     clearEvents: function() {
59608         
59609         if (!this.eventStore.getCount()) {
59610             return;
59611         }
59612         // reset number of rows in cells.
59613         Roo.each(this.cells.elements, function(c){
59614             c.rows = 0;
59615         });
59616         
59617         this.eventStore.each(function(e) {
59618             this.clearEvent(e);
59619         },this);
59620         
59621     },
59622     
59623     clearEvent : function(ev)
59624     {
59625         if (ev.els) {
59626             Roo.each(ev.els, function(el) {
59627                 el.un('mouseenter' ,this.onEventEnter, this);
59628                 el.un('mouseleave' ,this.onEventLeave, this);
59629                 el.remove();
59630             },this);
59631             ev.els = [];
59632         }
59633     },
59634     
59635     
59636     renderEvent : function(ev,ctr) {
59637         if (!ctr) {
59638              ctr = this.view.el.select('.fc-event-container',true).first();
59639         }
59640         
59641          
59642         this.clearEvent(ev);
59643             //code
59644        
59645         
59646         
59647         ev.els = [];
59648         var cells = ev.cells;
59649         var rows = ev.rows;
59650         this.fireEvent('eventrender', this, ev);
59651         
59652         for(var i =0; i < rows.length; i++) {
59653             
59654             cls = '';
59655             if (i == 0) {
59656                 cls += ' fc-event-start';
59657             }
59658             if ((i+1) == rows.length) {
59659                 cls += ' fc-event-end';
59660             }
59661             
59662             //Roo.log(ev.data);
59663             // how many rows should it span..
59664             var cg = this.eventTmpl.append(ctr,Roo.apply({
59665                 fccls : cls
59666                 
59667             }, ev.data) , true);
59668             
59669             
59670             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59671             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59672             cg.on('click', this.onEventClick, this, ev);
59673             
59674             ev.els.push(cg);
59675             
59676             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59677             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59678             //Roo.log(cg);
59679              
59680             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59681             cg.setWidth(ebox.right - sbox.x -2);
59682         }
59683     },
59684     
59685     renderEvents: function()
59686     {   
59687         // first make sure there is enough space..
59688         
59689         if (!this.eventTmpl) {
59690             this.eventTmpl = new Roo.Template(
59691                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59692                     '<div class="fc-event-inner">' +
59693                         '<span class="fc-event-time">{time}</span>' +
59694                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59695                     '</div>' +
59696                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59697                 '</div>'
59698             );
59699                 
59700         }
59701                
59702         
59703         
59704         this.cells.each(function(c) {
59705             //Roo.log(c.select('.fc-day-content div',true).first());
59706             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59707         });
59708         
59709         var ctr = this.view.el.select('.fc-event-container',true).first();
59710         
59711         var cls;
59712         this.eventStore.each(function(ev){
59713             
59714             this.renderEvent(ev);
59715              
59716              
59717         }, this);
59718         this.view.layout();
59719         
59720     },
59721     
59722     onEventEnter: function (e, el,event,d) {
59723         this.fireEvent('evententer', this, el, event);
59724     },
59725     
59726     onEventLeave: function (e, el,event,d) {
59727         this.fireEvent('eventleave', this, el, event);
59728     },
59729     
59730     onEventClick: function (e, el,event,d) {
59731         this.fireEvent('eventclick', this, el, event);
59732     },
59733     
59734     onMonthChange: function () {
59735         this.store.load();
59736     },
59737     
59738     onLoad: function () {
59739         
59740         //Roo.log('calendar onload');
59741 //         
59742         if(this.eventStore.getCount() > 0){
59743             
59744            
59745             
59746             this.eventStore.each(function(d){
59747                 
59748                 
59749                 // FIXME..
59750                 var add =   d.data;
59751                 if (typeof(add.end_dt) == 'undefined')  {
59752                     Roo.log("Missing End time in calendar data: ");
59753                     Roo.log(d);
59754                     return;
59755                 }
59756                 if (typeof(add.start_dt) == 'undefined')  {
59757                     Roo.log("Missing Start time in calendar data: ");
59758                     Roo.log(d);
59759                     return;
59760                 }
59761                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59762                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59763                 add.id = add.id || d.id;
59764                 add.title = add.title || '??';
59765                 
59766                 this.addItem(d);
59767                 
59768              
59769             },this);
59770         }
59771         
59772         this.renderEvents();
59773     }
59774     
59775
59776 });
59777 /*
59778  grid : {
59779                 xtype: 'Grid',
59780                 xns: Roo.grid,
59781                 listeners : {
59782                     render : function ()
59783                     {
59784                         _this.grid = this;
59785                         
59786                         if (!this.view.el.hasClass('course-timesheet')) {
59787                             this.view.el.addClass('course-timesheet');
59788                         }
59789                         if (this.tsStyle) {
59790                             this.ds.load({});
59791                             return; 
59792                         }
59793                         Roo.log('width');
59794                         Roo.log(_this.grid.view.el.getWidth());
59795                         
59796                         
59797                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59798                             '.course-timesheet .x-grid-row' : {
59799                                 height: '80px'
59800                             },
59801                             '.x-grid-row td' : {
59802                                 'vertical-align' : 0
59803                             },
59804                             '.course-edit-link' : {
59805                                 'color' : 'blue',
59806                                 'text-overflow' : 'ellipsis',
59807                                 'overflow' : 'hidden',
59808                                 'white-space' : 'nowrap',
59809                                 'cursor' : 'pointer'
59810                             },
59811                             '.sub-link' : {
59812                                 'color' : 'green'
59813                             },
59814                             '.de-act-sup-link' : {
59815                                 'color' : 'purple',
59816                                 'text-decoration' : 'line-through'
59817                             },
59818                             '.de-act-link' : {
59819                                 'color' : 'red',
59820                                 'text-decoration' : 'line-through'
59821                             },
59822                             '.course-timesheet .course-highlight' : {
59823                                 'border-top-style': 'dashed !important',
59824                                 'border-bottom-bottom': 'dashed !important'
59825                             },
59826                             '.course-timesheet .course-item' : {
59827                                 'font-family'   : 'tahoma, arial, helvetica',
59828                                 'font-size'     : '11px',
59829                                 'overflow'      : 'hidden',
59830                                 'padding-left'  : '10px',
59831                                 'padding-right' : '10px',
59832                                 'padding-top' : '10px' 
59833                             }
59834                             
59835                         }, Roo.id());
59836                                 this.ds.load({});
59837                     }
59838                 },
59839                 autoWidth : true,
59840                 monitorWindowResize : false,
59841                 cellrenderer : function(v,x,r)
59842                 {
59843                     return v;
59844                 },
59845                 sm : {
59846                     xtype: 'CellSelectionModel',
59847                     xns: Roo.grid
59848                 },
59849                 dataSource : {
59850                     xtype: 'Store',
59851                     xns: Roo.data,
59852                     listeners : {
59853                         beforeload : function (_self, options)
59854                         {
59855                             options.params = options.params || {};
59856                             options.params._month = _this.monthField.getValue();
59857                             options.params.limit = 9999;
59858                             options.params['sort'] = 'when_dt';    
59859                             options.params['dir'] = 'ASC';    
59860                             this.proxy.loadResponse = this.loadResponse;
59861                             Roo.log("load?");
59862                             //this.addColumns();
59863                         },
59864                         load : function (_self, records, options)
59865                         {
59866                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
59867                                 // if you click on the translation.. you can edit it...
59868                                 var el = Roo.get(this);
59869                                 var id = el.dom.getAttribute('data-id');
59870                                 var d = el.dom.getAttribute('data-date');
59871                                 var t = el.dom.getAttribute('data-time');
59872                                 //var id = this.child('span').dom.textContent;
59873                                 
59874                                 //Roo.log(this);
59875                                 Pman.Dialog.CourseCalendar.show({
59876                                     id : id,
59877                                     when_d : d,
59878                                     when_t : t,
59879                                     productitem_active : id ? 1 : 0
59880                                 }, function() {
59881                                     _this.grid.ds.load({});
59882                                 });
59883                            
59884                            });
59885                            
59886                            _this.panel.fireEvent('resize', [ '', '' ]);
59887                         }
59888                     },
59889                     loadResponse : function(o, success, response){
59890                             // this is overridden on before load..
59891                             
59892                             Roo.log("our code?");       
59893                             //Roo.log(success);
59894                             //Roo.log(response)
59895                             delete this.activeRequest;
59896                             if(!success){
59897                                 this.fireEvent("loadexception", this, o, response);
59898                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59899                                 return;
59900                             }
59901                             var result;
59902                             try {
59903                                 result = o.reader.read(response);
59904                             }catch(e){
59905                                 Roo.log("load exception?");
59906                                 this.fireEvent("loadexception", this, o, response, e);
59907                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59908                                 return;
59909                             }
59910                             Roo.log("ready...");        
59911                             // loop through result.records;
59912                             // and set this.tdate[date] = [] << array of records..
59913                             _this.tdata  = {};
59914                             Roo.each(result.records, function(r){
59915                                 //Roo.log(r.data);
59916                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
59917                                     _this.tdata[r.data.when_dt.format('j')] = [];
59918                                 }
59919                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
59920                             });
59921                             
59922                             //Roo.log(_this.tdata);
59923                             
59924                             result.records = [];
59925                             result.totalRecords = 6;
59926                     
59927                             // let's generate some duumy records for the rows.
59928                             //var st = _this.dateField.getValue();
59929                             
59930                             // work out monday..
59931                             //st = st.add(Date.DAY, -1 * st.format('w'));
59932                             
59933                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59934                             
59935                             var firstOfMonth = date.getFirstDayOfMonth();
59936                             var days = date.getDaysInMonth();
59937                             var d = 1;
59938                             var firstAdded = false;
59939                             for (var i = 0; i < result.totalRecords ; i++) {
59940                                 //var d= st.add(Date.DAY, i);
59941                                 var row = {};
59942                                 var added = 0;
59943                                 for(var w = 0 ; w < 7 ; w++){
59944                                     if(!firstAdded && firstOfMonth != w){
59945                                         continue;
59946                                     }
59947                                     if(d > days){
59948                                         continue;
59949                                     }
59950                                     firstAdded = true;
59951                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
59952                                     row['weekday'+w] = String.format(
59953                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
59954                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
59955                                                     d,
59956                                                     date.format('Y-m-')+dd
59957                                                 );
59958                                     added++;
59959                                     if(typeof(_this.tdata[d]) != 'undefined'){
59960                                         Roo.each(_this.tdata[d], function(r){
59961                                             var is_sub = '';
59962                                             var deactive = '';
59963                                             var id = r.id;
59964                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
59965                                             if(r.parent_id*1>0){
59966                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
59967                                                 id = r.parent_id;
59968                                             }
59969                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
59970                                                 deactive = 'de-act-link';
59971                                             }
59972                                             
59973                                             row['weekday'+w] += String.format(
59974                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
59975                                                     id, //0
59976                                                     r.product_id_name, //1
59977                                                     r.when_dt.format('h:ia'), //2
59978                                                     is_sub, //3
59979                                                     deactive, //4
59980                                                     desc // 5
59981                                             );
59982                                         });
59983                                     }
59984                                     d++;
59985                                 }
59986                                 
59987                                 // only do this if something added..
59988                                 if(added > 0){ 
59989                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
59990                                 }
59991                                 
59992                                 
59993                                 // push it twice. (second one with an hour..
59994                                 
59995                             }
59996                             //Roo.log(result);
59997                             this.fireEvent("load", this, o, o.request.arg);
59998                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
59999                         },
60000                     sortInfo : {field: 'when_dt', direction : 'ASC' },
60001                     proxy : {
60002                         xtype: 'HttpProxy',
60003                         xns: Roo.data,
60004                         method : 'GET',
60005                         url : baseURL + '/Roo/Shop_course.php'
60006                     },
60007                     reader : {
60008                         xtype: 'JsonReader',
60009                         xns: Roo.data,
60010                         id : 'id',
60011                         fields : [
60012                             {
60013                                 'name': 'id',
60014                                 'type': 'int'
60015                             },
60016                             {
60017                                 'name': 'when_dt',
60018                                 'type': 'string'
60019                             },
60020                             {
60021                                 'name': 'end_dt',
60022                                 'type': 'string'
60023                             },
60024                             {
60025                                 'name': 'parent_id',
60026                                 'type': 'int'
60027                             },
60028                             {
60029                                 'name': 'product_id',
60030                                 'type': 'int'
60031                             },
60032                             {
60033                                 'name': 'productitem_id',
60034                                 'type': 'int'
60035                             },
60036                             {
60037                                 'name': 'guid',
60038                                 'type': 'int'
60039                             }
60040                         ]
60041                     }
60042                 },
60043                 toolbar : {
60044                     xtype: 'Toolbar',
60045                     xns: Roo,
60046                     items : [
60047                         {
60048                             xtype: 'Button',
60049                             xns: Roo.Toolbar,
60050                             listeners : {
60051                                 click : function (_self, e)
60052                                 {
60053                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60054                                     sd.setMonth(sd.getMonth()-1);
60055                                     _this.monthField.setValue(sd.format('Y-m-d'));
60056                                     _this.grid.ds.load({});
60057                                 }
60058                             },
60059                             text : "Back"
60060                         },
60061                         {
60062                             xtype: 'Separator',
60063                             xns: Roo.Toolbar
60064                         },
60065                         {
60066                             xtype: 'MonthField',
60067                             xns: Roo.form,
60068                             listeners : {
60069                                 render : function (_self)
60070                                 {
60071                                     _this.monthField = _self;
60072                                    // _this.monthField.set  today
60073                                 },
60074                                 select : function (combo, date)
60075                                 {
60076                                     _this.grid.ds.load({});
60077                                 }
60078                             },
60079                             value : (function() { return new Date(); })()
60080                         },
60081                         {
60082                             xtype: 'Separator',
60083                             xns: Roo.Toolbar
60084                         },
60085                         {
60086                             xtype: 'TextItem',
60087                             xns: Roo.Toolbar,
60088                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60089                         },
60090                         {
60091                             xtype: 'Fill',
60092                             xns: Roo.Toolbar
60093                         },
60094                         {
60095                             xtype: 'Button',
60096                             xns: Roo.Toolbar,
60097                             listeners : {
60098                                 click : function (_self, e)
60099                                 {
60100                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60101                                     sd.setMonth(sd.getMonth()+1);
60102                                     _this.monthField.setValue(sd.format('Y-m-d'));
60103                                     _this.grid.ds.load({});
60104                                 }
60105                             },
60106                             text : "Next"
60107                         }
60108                     ]
60109                 },
60110                  
60111             }
60112         };
60113         
60114         *//*
60115  * Based on:
60116  * Ext JS Library 1.1.1
60117  * Copyright(c) 2006-2007, Ext JS, LLC.
60118  *
60119  * Originally Released Under LGPL - original licence link has changed is not relivant.
60120  *
60121  * Fork - LGPL
60122  * <script type="text/javascript">
60123  */
60124  
60125 /**
60126  * @class Roo.LoadMask
60127  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60128  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60129  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60130  * element's UpdateManager load indicator and will be destroyed after the initial load.
60131  * @constructor
60132  * Create a new LoadMask
60133  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60134  * @param {Object} config The config object
60135  */
60136 Roo.LoadMask = function(el, config){
60137     this.el = Roo.get(el);
60138     Roo.apply(this, config);
60139     if(this.store){
60140         this.store.on('beforeload', this.onBeforeLoad, this);
60141         this.store.on('load', this.onLoad, this);
60142         this.store.on('loadexception', this.onLoadException, this);
60143         this.removeMask = false;
60144     }else{
60145         var um = this.el.getUpdateManager();
60146         um.showLoadIndicator = false; // disable the default indicator
60147         um.on('beforeupdate', this.onBeforeLoad, this);
60148         um.on('update', this.onLoad, this);
60149         um.on('failure', this.onLoad, this);
60150         this.removeMask = true;
60151     }
60152 };
60153
60154 Roo.LoadMask.prototype = {
60155     /**
60156      * @cfg {Boolean} removeMask
60157      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60158      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60159      */
60160     /**
60161      * @cfg {String} msg
60162      * The text to display in a centered loading message box (defaults to 'Loading...')
60163      */
60164     msg : 'Loading...',
60165     /**
60166      * @cfg {String} msgCls
60167      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60168      */
60169     msgCls : 'x-mask-loading',
60170
60171     /**
60172      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60173      * @type Boolean
60174      */
60175     disabled: false,
60176
60177     /**
60178      * Disables the mask to prevent it from being displayed
60179      */
60180     disable : function(){
60181        this.disabled = true;
60182     },
60183
60184     /**
60185      * Enables the mask so that it can be displayed
60186      */
60187     enable : function(){
60188         this.disabled = false;
60189     },
60190     
60191     onLoadException : function()
60192     {
60193         Roo.log(arguments);
60194         
60195         if (typeof(arguments[3]) != 'undefined') {
60196             Roo.MessageBox.alert("Error loading",arguments[3]);
60197         } 
60198         /*
60199         try {
60200             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60201                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60202             }   
60203         } catch(e) {
60204             
60205         }
60206         */
60207     
60208         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60209     },
60210     // private
60211     onLoad : function()
60212     {
60213         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60214     },
60215
60216     // private
60217     onBeforeLoad : function(){
60218         if(!this.disabled){
60219             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
60220         }
60221     },
60222
60223     // private
60224     destroy : function(){
60225         if(this.store){
60226             this.store.un('beforeload', this.onBeforeLoad, this);
60227             this.store.un('load', this.onLoad, this);
60228             this.store.un('loadexception', this.onLoadException, this);
60229         }else{
60230             var um = this.el.getUpdateManager();
60231             um.un('beforeupdate', this.onBeforeLoad, this);
60232             um.un('update', this.onLoad, this);
60233             um.un('failure', this.onLoad, this);
60234         }
60235     }
60236 };/*
60237  * Based on:
60238  * Ext JS Library 1.1.1
60239  * Copyright(c) 2006-2007, Ext JS, LLC.
60240  *
60241  * Originally Released Under LGPL - original licence link has changed is not relivant.
60242  *
60243  * Fork - LGPL
60244  * <script type="text/javascript">
60245  */
60246
60247
60248 /**
60249  * @class Roo.XTemplate
60250  * @extends Roo.Template
60251  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60252 <pre><code>
60253 var t = new Roo.XTemplate(
60254         '&lt;select name="{name}"&gt;',
60255                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60256         '&lt;/select&gt;'
60257 );
60258  
60259 // then append, applying the master template values
60260  </code></pre>
60261  *
60262  * Supported features:
60263  *
60264  *  Tags:
60265
60266 <pre><code>
60267       {a_variable} - output encoded.
60268       {a_variable.format:("Y-m-d")} - call a method on the variable
60269       {a_variable:raw} - unencoded output
60270       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60271       {a_variable:this.method_on_template(...)} - call a method on the template object.
60272  
60273 </code></pre>
60274  *  The tpl tag:
60275 <pre><code>
60276         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60277         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60278         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60279         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60280   
60281         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60282         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60283 </code></pre>
60284  *      
60285  */
60286 Roo.XTemplate = function()
60287 {
60288     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60289     if (this.html) {
60290         this.compile();
60291     }
60292 };
60293
60294
60295 Roo.extend(Roo.XTemplate, Roo.Template, {
60296
60297     /**
60298      * The various sub templates
60299      */
60300     tpls : false,
60301     /**
60302      *
60303      * basic tag replacing syntax
60304      * WORD:WORD()
60305      *
60306      * // you can fake an object call by doing this
60307      *  x.t:(test,tesT) 
60308      * 
60309      */
60310     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60311
60312     /**
60313      * compile the template
60314      *
60315      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60316      *
60317      */
60318     compile: function()
60319     {
60320         var s = this.html;
60321      
60322         s = ['<tpl>', s, '</tpl>'].join('');
60323     
60324         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60325             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60326             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60327             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60328             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60329             m,
60330             id     = 0,
60331             tpls   = [];
60332     
60333         while(true == !!(m = s.match(re))){
60334             var forMatch   = m[0].match(nameRe),
60335                 ifMatch   = m[0].match(ifRe),
60336                 execMatch   = m[0].match(execRe),
60337                 namedMatch   = m[0].match(namedRe),
60338                 
60339                 exp  = null, 
60340                 fn   = null,
60341                 exec = null,
60342                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60343                 
60344             if (ifMatch) {
60345                 // if - puts fn into test..
60346                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60347                 if(exp){
60348                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60349                 }
60350             }
60351             
60352             if (execMatch) {
60353                 // exec - calls a function... returns empty if true is  returned.
60354                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60355                 if(exp){
60356                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60357                 }
60358             }
60359             
60360             
60361             if (name) {
60362                 // for = 
60363                 switch(name){
60364                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60365                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60366                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60367                 }
60368             }
60369             var uid = namedMatch ? namedMatch[1] : id;
60370             
60371             
60372             tpls.push({
60373                 id:     namedMatch ? namedMatch[1] : id,
60374                 target: name,
60375                 exec:   exec,
60376                 test:   fn,
60377                 body:   m[1] || ''
60378             });
60379             if (namedMatch) {
60380                 s = s.replace(m[0], '');
60381             } else { 
60382                 s = s.replace(m[0], '{xtpl'+ id + '}');
60383             }
60384             ++id;
60385         }
60386         this.tpls = [];
60387         for(var i = tpls.length-1; i >= 0; --i){
60388             this.compileTpl(tpls[i]);
60389             this.tpls[tpls[i].id] = tpls[i];
60390         }
60391         this.master = tpls[tpls.length-1];
60392         return this;
60393     },
60394     /**
60395      * same as applyTemplate, except it's done to one of the subTemplates
60396      * when using named templates, you can do:
60397      *
60398      * var str = pl.applySubTemplate('your-name', values);
60399      *
60400      * 
60401      * @param {Number} id of the template
60402      * @param {Object} values to apply to template
60403      * @param {Object} parent (normaly the instance of this object)
60404      */
60405     applySubTemplate : function(id, values, parent)
60406     {
60407         
60408         
60409         var t = this.tpls[id];
60410         
60411         
60412         try { 
60413             if(t.test && !t.test.call(this, values, parent)){
60414                 return '';
60415             }
60416         } catch(e) {
60417             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60418             Roo.log(e.toString());
60419             Roo.log(t.test);
60420             return ''
60421         }
60422         try { 
60423             
60424             if(t.exec && t.exec.call(this, values, parent)){
60425                 return '';
60426             }
60427         } catch(e) {
60428             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60429             Roo.log(e.toString());
60430             Roo.log(t.exec);
60431             return ''
60432         }
60433         try {
60434             var vs = t.target ? t.target.call(this, values, parent) : values;
60435             parent = t.target ? values : parent;
60436             if(t.target && vs instanceof Array){
60437                 var buf = [];
60438                 for(var i = 0, len = vs.length; i < len; i++){
60439                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60440                 }
60441                 return buf.join('');
60442             }
60443             return t.compiled.call(this, vs, parent);
60444         } catch (e) {
60445             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60446             Roo.log(e.toString());
60447             Roo.log(t.compiled);
60448             return '';
60449         }
60450     },
60451
60452     compileTpl : function(tpl)
60453     {
60454         var fm = Roo.util.Format;
60455         var useF = this.disableFormats !== true;
60456         var sep = Roo.isGecko ? "+" : ",";
60457         var undef = function(str) {
60458             Roo.log("Property not found :"  + str);
60459             return '';
60460         };
60461         
60462         var fn = function(m, name, format, args)
60463         {
60464             //Roo.log(arguments);
60465             args = args ? args.replace(/\\'/g,"'") : args;
60466             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60467             if (typeof(format) == 'undefined') {
60468                 format= 'htmlEncode';
60469             }
60470             if (format == 'raw' ) {
60471                 format = false;
60472             }
60473             
60474             if(name.substr(0, 4) == 'xtpl'){
60475                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60476             }
60477             
60478             // build an array of options to determine if value is undefined..
60479             
60480             // basically get 'xxxx.yyyy' then do
60481             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60482             //    (function () { Roo.log("Property not found"); return ''; })() :
60483             //    ......
60484             
60485             var udef_ar = [];
60486             var lookfor = '';
60487             Roo.each(name.split('.'), function(st) {
60488                 lookfor += (lookfor.length ? '.': '') + st;
60489                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60490             });
60491             
60492             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60493             
60494             
60495             if(format && useF){
60496                 
60497                 args = args ? ',' + args : "";
60498                  
60499                 if(format.substr(0, 5) != "this."){
60500                     format = "fm." + format + '(';
60501                 }else{
60502                     format = 'this.call("'+ format.substr(5) + '", ';
60503                     args = ", values";
60504                 }
60505                 
60506                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60507             }
60508              
60509             if (args.length) {
60510                 // called with xxyx.yuu:(test,test)
60511                 // change to ()
60512                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60513             }
60514             // raw.. - :raw modifier..
60515             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60516             
60517         };
60518         var body;
60519         // branched to use + in gecko and [].join() in others
60520         if(Roo.isGecko){
60521             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60522                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60523                     "';};};";
60524         }else{
60525             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60526             body.push(tpl.body.replace(/(\r\n|\n)/g,
60527                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60528             body.push("'].join('');};};");
60529             body = body.join('');
60530         }
60531         
60532         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60533        
60534         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60535         eval(body);
60536         
60537         return this;
60538     },
60539
60540     applyTemplate : function(values){
60541         return this.master.compiled.call(this, values, {});
60542         //var s = this.subs;
60543     },
60544
60545     apply : function(){
60546         return this.applyTemplate.apply(this, arguments);
60547     }
60548
60549  });
60550
60551 Roo.XTemplate.from = function(el){
60552     el = Roo.getDom(el);
60553     return new Roo.XTemplate(el.value || el.innerHTML);
60554 };