roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         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             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7198         },
7199
7200         /**
7201          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7202          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7203          * @param {String} selector The simple selector to test
7204          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7205                 search as a number or element (defaults to 10 || document.body)
7206          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7207          */
7208         up : function(simpleSelector, maxDepth){
7209             return this.findParentNode(simpleSelector, maxDepth, true);
7210         },
7211
7212
7213
7214         /**
7215          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7216          * @param {String} selector The simple selector to test
7217          * @return {Boolean} True if this element matches the selector, else false
7218          */
7219         is : function(simpleSelector){
7220             return Roo.DomQuery.is(this.dom, simpleSelector);
7221         },
7222
7223         /**
7224          * Perform animation on this element.
7225          * @param {Object} args The YUI animation control args
7226          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7227          * @param {Function} onComplete (optional) Function to call when animation completes
7228          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7229          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7230          * @return {Roo.Element} this
7231          */
7232         animate : function(args, duration, onComplete, easing, animType){
7233             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7234             return this;
7235         },
7236
7237         /*
7238          * @private Internal animation call
7239          */
7240         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7241             animType = animType || 'run';
7242             opt = opt || {};
7243             var anim = Roo.lib.Anim[animType](
7244                 this.dom, args,
7245                 (opt.duration || defaultDur) || .35,
7246                 (opt.easing || defaultEase) || 'easeOut',
7247                 function(){
7248                     Roo.callback(cb, this);
7249                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7250                 },
7251                 this
7252             );
7253             opt.anim = anim;
7254             return anim;
7255         },
7256
7257         // private legacy anim prep
7258         preanim : function(a, i){
7259             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7260         },
7261
7262         /**
7263          * Removes worthless text nodes
7264          * @param {Boolean} forceReclean (optional) By default the element
7265          * keeps track if it has been cleaned already so
7266          * you can call this over and over. However, if you update the element and
7267          * need to force a reclean, you can pass true.
7268          */
7269         clean : function(forceReclean){
7270             if(this.isCleaned && forceReclean !== true){
7271                 return this;
7272             }
7273             var ns = /\S/;
7274             var d = this.dom, n = d.firstChild, ni = -1;
7275             while(n){
7276                 var nx = n.nextSibling;
7277                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7278                     d.removeChild(n);
7279                 }else{
7280                     n.nodeIndex = ++ni;
7281                 }
7282                 n = nx;
7283             }
7284             this.isCleaned = true;
7285             return this;
7286         },
7287
7288         // private
7289         calcOffsetsTo : function(el){
7290             el = Roo.get(el);
7291             var d = el.dom;
7292             var restorePos = false;
7293             if(el.getStyle('position') == 'static'){
7294                 el.position('relative');
7295                 restorePos = true;
7296             }
7297             var x = 0, y =0;
7298             var op = this.dom;
7299             while(op && op != d && op.tagName != 'HTML'){
7300                 x+= op.offsetLeft;
7301                 y+= op.offsetTop;
7302                 op = op.offsetParent;
7303             }
7304             if(restorePos){
7305                 el.position('static');
7306             }
7307             return [x, y];
7308         },
7309
7310         /**
7311          * Scrolls this element into view within the passed container.
7312          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7313          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7314          * @return {Roo.Element} this
7315          */
7316         scrollIntoView : function(container, hscroll){
7317             var c = Roo.getDom(container) || document.body;
7318             var el = this.dom;
7319
7320             var o = this.calcOffsetsTo(c),
7321                 l = o[0],
7322                 t = o[1],
7323                 b = t+el.offsetHeight,
7324                 r = l+el.offsetWidth;
7325
7326             var ch = c.clientHeight;
7327             var ct = parseInt(c.scrollTop, 10);
7328             var cl = parseInt(c.scrollLeft, 10);
7329             var cb = ct + ch;
7330             var cr = cl + c.clientWidth;
7331
7332             if(t < ct){
7333                 c.scrollTop = t;
7334             }else if(b > cb){
7335                 c.scrollTop = b-ch;
7336             }
7337
7338             if(hscroll !== false){
7339                 if(l < cl){
7340                     c.scrollLeft = l;
7341                 }else if(r > cr){
7342                     c.scrollLeft = r-c.clientWidth;
7343                 }
7344             }
7345             return this;
7346         },
7347
7348         // private
7349         scrollChildIntoView : function(child, hscroll){
7350             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7351         },
7352
7353         /**
7354          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7355          * the new height may not be available immediately.
7356          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7357          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7358          * @param {Function} onComplete (optional) Function to call when animation completes
7359          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7360          * @return {Roo.Element} this
7361          */
7362         autoHeight : function(animate, duration, onComplete, easing){
7363             var oldHeight = this.getHeight();
7364             this.clip();
7365             this.setHeight(1); // force clipping
7366             setTimeout(function(){
7367                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7368                 if(!animate){
7369                     this.setHeight(height);
7370                     this.unclip();
7371                     if(typeof onComplete == "function"){
7372                         onComplete();
7373                     }
7374                 }else{
7375                     this.setHeight(oldHeight); // restore original height
7376                     this.setHeight(height, animate, duration, function(){
7377                         this.unclip();
7378                         if(typeof onComplete == "function") { onComplete(); }
7379                     }.createDelegate(this), easing);
7380                 }
7381             }.createDelegate(this), 0);
7382             return this;
7383         },
7384
7385         /**
7386          * Returns true if this element is an ancestor of the passed element
7387          * @param {HTMLElement/String} el The element to check
7388          * @return {Boolean} True if this element is an ancestor of el, else false
7389          */
7390         contains : function(el){
7391             if(!el){return false;}
7392             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7393         },
7394
7395         /**
7396          * Checks whether the element is currently visible using both visibility and display properties.
7397          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7398          * @return {Boolean} True if the element is currently visible, else false
7399          */
7400         isVisible : function(deep) {
7401             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7402             if(deep !== true || !vis){
7403                 return vis;
7404             }
7405             var p = this.dom.parentNode;
7406             while(p && p.tagName.toLowerCase() != "body"){
7407                 if(!Roo.fly(p, '_isVisible').isVisible()){
7408                     return false;
7409                 }
7410                 p = p.parentNode;
7411             }
7412             return true;
7413         },
7414
7415         /**
7416          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7417          * @param {String} selector The CSS selector
7418          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7419          * @return {CompositeElement/CompositeElementLite} The composite element
7420          */
7421         select : function(selector, unique){
7422             return El.select(selector, unique, this.dom);
7423         },
7424
7425         /**
7426          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7427          * @param {String} selector The CSS selector
7428          * @return {Array} An array of the matched nodes
7429          */
7430         query : function(selector, unique){
7431             return Roo.DomQuery.select(selector, this.dom);
7432         },
7433
7434         /**
7435          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7436          * @param {String} selector The CSS selector
7437          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7438          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7439          */
7440         child : function(selector, returnDom){
7441             var n = Roo.DomQuery.selectNode(selector, this.dom);
7442             return returnDom ? n : Roo.get(n);
7443         },
7444
7445         /**
7446          * Selects a single *direct* child 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         down : function(selector, returnDom){
7452             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7453             return returnDom ? n : Roo.get(n);
7454         },
7455
7456         /**
7457          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7458          * @param {String} group The group the DD object is member of
7459          * @param {Object} config The DD config object
7460          * @param {Object} overrides An object containing methods to override/implement on the DD object
7461          * @return {Roo.dd.DD} The DD object
7462          */
7463         initDD : function(group, config, overrides){
7464             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7465             return Roo.apply(dd, overrides);
7466         },
7467
7468         /**
7469          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7470          * @param {String} group The group the DDProxy object is member of
7471          * @param {Object} config The DDProxy config object
7472          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7473          * @return {Roo.dd.DDProxy} The DDProxy object
7474          */
7475         initDDProxy : function(group, config, overrides){
7476             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7477             return Roo.apply(dd, overrides);
7478         },
7479
7480         /**
7481          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7482          * @param {String} group The group the DDTarget object is member of
7483          * @param {Object} config The DDTarget config object
7484          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7485          * @return {Roo.dd.DDTarget} The DDTarget object
7486          */
7487         initDDTarget : function(group, config, overrides){
7488             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7489             return Roo.apply(dd, overrides);
7490         },
7491
7492         /**
7493          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7494          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7495          * @param {Boolean} visible Whether the element is visible
7496          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7497          * @return {Roo.Element} this
7498          */
7499          setVisible : function(visible, animate){
7500             if(!animate || !A){
7501                 if(this.visibilityMode == El.DISPLAY){
7502                     this.setDisplayed(visible);
7503                 }else{
7504                     this.fixDisplay();
7505                     this.dom.style.visibility = visible ? "visible" : "hidden";
7506                 }
7507             }else{
7508                 // closure for composites
7509                 var dom = this.dom;
7510                 var visMode = this.visibilityMode;
7511                 if(visible){
7512                     this.setOpacity(.01);
7513                     this.setVisible(true);
7514                 }
7515                 this.anim({opacity: { to: (visible?1:0) }},
7516                       this.preanim(arguments, 1),
7517                       null, .35, 'easeIn', function(){
7518                          if(!visible){
7519                              if(visMode == El.DISPLAY){
7520                                  dom.style.display = "none";
7521                              }else{
7522                                  dom.style.visibility = "hidden";
7523                              }
7524                              Roo.get(dom).setOpacity(1);
7525                          }
7526                      });
7527             }
7528             return this;
7529         },
7530
7531         /**
7532          * Returns true if display is not "none"
7533          * @return {Boolean}
7534          */
7535         isDisplayed : function() {
7536             return this.getStyle("display") != "none";
7537         },
7538
7539         /**
7540          * Toggles the element's visibility or display, depending on visibility mode.
7541          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7542          * @return {Roo.Element} this
7543          */
7544         toggle : function(animate){
7545             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7546             return this;
7547         },
7548
7549         /**
7550          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7551          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7552          * @return {Roo.Element} this
7553          */
7554         setDisplayed : function(value) {
7555             if(typeof value == "boolean"){
7556                value = value ? this.originalDisplay : "none";
7557             }
7558             this.setStyle("display", value);
7559             return this;
7560         },
7561
7562         /**
7563          * Tries to focus the element. Any exceptions are caught and ignored.
7564          * @return {Roo.Element} this
7565          */
7566         focus : function() {
7567             try{
7568                 this.dom.focus();
7569             }catch(e){}
7570             return this;
7571         },
7572
7573         /**
7574          * Tries to blur the element. Any exceptions are caught and ignored.
7575          * @return {Roo.Element} this
7576          */
7577         blur : function() {
7578             try{
7579                 this.dom.blur();
7580             }catch(e){}
7581             return this;
7582         },
7583
7584         /**
7585          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7586          * @param {String/Array} className The CSS class to add, or an array of classes
7587          * @return {Roo.Element} this
7588          */
7589         addClass : function(className){
7590             if(className instanceof Array){
7591                 for(var i = 0, len = className.length; i < len; i++) {
7592                     this.addClass(className[i]);
7593                 }
7594             }else{
7595                 if(className && !this.hasClass(className)){
7596                     this.dom.className = this.dom.className + " " + className;
7597                 }
7598             }
7599             return this;
7600         },
7601
7602         /**
7603          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7604          * @param {String/Array} className The CSS class to add, or an array of classes
7605          * @return {Roo.Element} this
7606          */
7607         radioClass : function(className){
7608             var siblings = this.dom.parentNode.childNodes;
7609             for(var i = 0; i < siblings.length; i++) {
7610                 var s = siblings[i];
7611                 if(s.nodeType == 1){
7612                     Roo.get(s).removeClass(className);
7613                 }
7614             }
7615             this.addClass(className);
7616             return this;
7617         },
7618
7619         /**
7620          * Removes one or more CSS classes from the element.
7621          * @param {String/Array} className The CSS class to remove, or an array of classes
7622          * @return {Roo.Element} this
7623          */
7624         removeClass : function(className){
7625             if(!className || !this.dom.className){
7626                 return this;
7627             }
7628             if(className instanceof Array){
7629                 for(var i = 0, len = className.length; i < len; i++) {
7630                     this.removeClass(className[i]);
7631                 }
7632             }else{
7633                 if(this.hasClass(className)){
7634                     var re = this.classReCache[className];
7635                     if (!re) {
7636                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7637                        this.classReCache[className] = re;
7638                     }
7639                     this.dom.className =
7640                         this.dom.className.replace(re, " ");
7641                 }
7642             }
7643             return this;
7644         },
7645
7646         // private
7647         classReCache: {},
7648
7649         /**
7650          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7651          * @param {String} className The CSS class to toggle
7652          * @return {Roo.Element} this
7653          */
7654         toggleClass : function(className){
7655             if(this.hasClass(className)){
7656                 this.removeClass(className);
7657             }else{
7658                 this.addClass(className);
7659             }
7660             return this;
7661         },
7662
7663         /**
7664          * Checks if the specified CSS class exists on this element's DOM node.
7665          * @param {String} className The CSS class to check for
7666          * @return {Boolean} True if the class exists, else false
7667          */
7668         hasClass : function(className){
7669             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7670         },
7671
7672         /**
7673          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7674          * @param {String} oldClassName The CSS class to replace
7675          * @param {String} newClassName The replacement CSS class
7676          * @return {Roo.Element} this
7677          */
7678         replaceClass : function(oldClassName, newClassName){
7679             this.removeClass(oldClassName);
7680             this.addClass(newClassName);
7681             return this;
7682         },
7683
7684         /**
7685          * Returns an object with properties matching the styles requested.
7686          * For example, el.getStyles('color', 'font-size', 'width') might return
7687          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7688          * @param {String} style1 A style name
7689          * @param {String} style2 A style name
7690          * @param {String} etc.
7691          * @return {Object} The style object
7692          */
7693         getStyles : function(){
7694             var a = arguments, len = a.length, r = {};
7695             for(var i = 0; i < len; i++){
7696                 r[a[i]] = this.getStyle(a[i]);
7697             }
7698             return r;
7699         },
7700
7701         /**
7702          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7703          * @param {String} property The style property whose value is returned.
7704          * @return {String} The current value of the style property for this element.
7705          */
7706         getStyle : function(){
7707             return view && view.getComputedStyle ?
7708                 function(prop){
7709                     var el = this.dom, v, cs, camel;
7710                     if(prop == 'float'){
7711                         prop = "cssFloat";
7712                     }
7713                     if(el.style && (v = el.style[prop])){
7714                         return v;
7715                     }
7716                     if(cs = view.getComputedStyle(el, "")){
7717                         if(!(camel = propCache[prop])){
7718                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7719                         }
7720                         return cs[camel];
7721                     }
7722                     return null;
7723                 } :
7724                 function(prop){
7725                     var el = this.dom, v, cs, camel;
7726                     if(prop == 'opacity'){
7727                         if(typeof el.style.filter == 'string'){
7728                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7729                             if(m){
7730                                 var fv = parseFloat(m[1]);
7731                                 if(!isNaN(fv)){
7732                                     return fv ? fv / 100 : 0;
7733                                 }
7734                             }
7735                         }
7736                         return 1;
7737                     }else if(prop == 'float'){
7738                         prop = "styleFloat";
7739                     }
7740                     if(!(camel = propCache[prop])){
7741                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7742                     }
7743                     if(v = el.style[camel]){
7744                         return v;
7745                     }
7746                     if(cs = el.currentStyle){
7747                         return cs[camel];
7748                     }
7749                     return null;
7750                 };
7751         }(),
7752
7753         /**
7754          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7755          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7756          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7757          * @return {Roo.Element} this
7758          */
7759         setStyle : function(prop, value){
7760             if(typeof prop == "string"){
7761                 
7762                 if (prop == 'float') {
7763                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7764                     return this;
7765                 }
7766                 
7767                 var camel;
7768                 if(!(camel = propCache[prop])){
7769                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7770                 }
7771                 
7772                 if(camel == 'opacity') {
7773                     this.setOpacity(value);
7774                 }else{
7775                     this.dom.style[camel] = value;
7776                 }
7777             }else{
7778                 for(var style in prop){
7779                     if(typeof prop[style] != "function"){
7780                        this.setStyle(style, prop[style]);
7781                     }
7782                 }
7783             }
7784             return this;
7785         },
7786
7787         /**
7788          * More flexible version of {@link #setStyle} for setting style properties.
7789          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7790          * a function which returns such a specification.
7791          * @return {Roo.Element} this
7792          */
7793         applyStyles : function(style){
7794             Roo.DomHelper.applyStyles(this.dom, style);
7795             return this;
7796         },
7797
7798         /**
7799           * 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).
7800           * @return {Number} The X position of the element
7801           */
7802         getX : function(){
7803             return D.getX(this.dom);
7804         },
7805
7806         /**
7807           * 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).
7808           * @return {Number} The Y position of the element
7809           */
7810         getY : function(){
7811             return D.getY(this.dom);
7812         },
7813
7814         /**
7815           * 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).
7816           * @return {Array} The XY position of the element
7817           */
7818         getXY : function(){
7819             return D.getXY(this.dom);
7820         },
7821
7822         /**
7823          * 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).
7824          * @param {Number} The X position of the element
7825          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7826          * @return {Roo.Element} this
7827          */
7828         setX : function(x, animate){
7829             if(!animate || !A){
7830                 D.setX(this.dom, x);
7831             }else{
7832                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7833             }
7834             return this;
7835         },
7836
7837         /**
7838          * 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).
7839          * @param {Number} The Y position of the element
7840          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7841          * @return {Roo.Element} this
7842          */
7843         setY : function(y, animate){
7844             if(!animate || !A){
7845                 D.setY(this.dom, y);
7846             }else{
7847                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7848             }
7849             return this;
7850         },
7851
7852         /**
7853          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7854          * @param {String} left The left CSS property value
7855          * @return {Roo.Element} this
7856          */
7857         setLeft : function(left){
7858             this.setStyle("left", this.addUnits(left));
7859             return this;
7860         },
7861
7862         /**
7863          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7864          * @param {String} top The top CSS property value
7865          * @return {Roo.Element} this
7866          */
7867         setTop : function(top){
7868             this.setStyle("top", this.addUnits(top));
7869             return this;
7870         },
7871
7872         /**
7873          * Sets the element's CSS right style.
7874          * @param {String} right The right CSS property value
7875          * @return {Roo.Element} this
7876          */
7877         setRight : function(right){
7878             this.setStyle("right", this.addUnits(right));
7879             return this;
7880         },
7881
7882         /**
7883          * Sets the element's CSS bottom style.
7884          * @param {String} bottom The bottom CSS property value
7885          * @return {Roo.Element} this
7886          */
7887         setBottom : function(bottom){
7888             this.setStyle("bottom", this.addUnits(bottom));
7889             return this;
7890         },
7891
7892         /**
7893          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7894          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7895          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7896          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7897          * @return {Roo.Element} this
7898          */
7899         setXY : function(pos, animate){
7900             if(!animate || !A){
7901                 D.setXY(this.dom, pos);
7902             }else{
7903                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7904             }
7905             return this;
7906         },
7907
7908         /**
7909          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7910          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7911          * @param {Number} x X value for new position (coordinates are page-based)
7912          * @param {Number} y Y value for new position (coordinates are page-based)
7913          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7914          * @return {Roo.Element} this
7915          */
7916         setLocation : function(x, y, animate){
7917             this.setXY([x, y], this.preanim(arguments, 2));
7918             return this;
7919         },
7920
7921         /**
7922          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7923          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7924          * @param {Number} x X value for new position (coordinates are page-based)
7925          * @param {Number} y Y value for new position (coordinates are page-based)
7926          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7927          * @return {Roo.Element} this
7928          */
7929         moveTo : function(x, y, animate){
7930             this.setXY([x, y], this.preanim(arguments, 2));
7931             return this;
7932         },
7933
7934         /**
7935          * Returns the region of the given element.
7936          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7937          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7938          */
7939         getRegion : function(){
7940             return D.getRegion(this.dom);
7941         },
7942
7943         /**
7944          * Returns the offset height of the element
7945          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7946          * @return {Number} The element's height
7947          */
7948         getHeight : function(contentHeight){
7949             var h = this.dom.offsetHeight || 0;
7950             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7951         },
7952
7953         /**
7954          * Returns the offset width of the element
7955          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7956          * @return {Number} The element's width
7957          */
7958         getWidth : function(contentWidth){
7959             var w = this.dom.offsetWidth || 0;
7960             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7961         },
7962
7963         /**
7964          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7965          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7966          * if a height has not been set using CSS.
7967          * @return {Number}
7968          */
7969         getComputedHeight : function(){
7970             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7971             if(!h){
7972                 h = parseInt(this.getStyle('height'), 10) || 0;
7973                 if(!this.isBorderBox()){
7974                     h += this.getFrameWidth('tb');
7975                 }
7976             }
7977             return h;
7978         },
7979
7980         /**
7981          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7982          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7983          * if a width has not been set using CSS.
7984          * @return {Number}
7985          */
7986         getComputedWidth : function(){
7987             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7988             if(!w){
7989                 w = parseInt(this.getStyle('width'), 10) || 0;
7990                 if(!this.isBorderBox()){
7991                     w += this.getFrameWidth('lr');
7992                 }
7993             }
7994             return w;
7995         },
7996
7997         /**
7998          * Returns the size of the element.
7999          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8000          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8001          */
8002         getSize : function(contentSize){
8003             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8004         },
8005
8006         /**
8007          * Returns the width and height of the viewport.
8008          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8009          */
8010         getViewSize : function(){
8011             var d = this.dom, doc = document, aw = 0, ah = 0;
8012             if(d == doc || d == doc.body){
8013                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8014             }else{
8015                 return {
8016                     width : d.clientWidth,
8017                     height: d.clientHeight
8018                 };
8019             }
8020         },
8021
8022         /**
8023          * Returns the value of the "value" attribute
8024          * @param {Boolean} asNumber true to parse the value as a number
8025          * @return {String/Number}
8026          */
8027         getValue : function(asNumber){
8028             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8029         },
8030
8031         // private
8032         adjustWidth : function(width){
8033             if(typeof width == "number"){
8034                 if(this.autoBoxAdjust && !this.isBorderBox()){
8035                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8036                 }
8037                 if(width < 0){
8038                     width = 0;
8039                 }
8040             }
8041             return width;
8042         },
8043
8044         // private
8045         adjustHeight : function(height){
8046             if(typeof height == "number"){
8047                if(this.autoBoxAdjust && !this.isBorderBox()){
8048                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8049                }
8050                if(height < 0){
8051                    height = 0;
8052                }
8053             }
8054             return height;
8055         },
8056
8057         /**
8058          * Set the width of the element
8059          * @param {Number} width The new width
8060          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8061          * @return {Roo.Element} this
8062          */
8063         setWidth : function(width, animate){
8064             width = this.adjustWidth(width);
8065             if(!animate || !A){
8066                 this.dom.style.width = this.addUnits(width);
8067             }else{
8068                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8069             }
8070             return this;
8071         },
8072
8073         /**
8074          * Set the height of the element
8075          * @param {Number} height The new height
8076          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8077          * @return {Roo.Element} this
8078          */
8079          setHeight : function(height, animate){
8080             height = this.adjustHeight(height);
8081             if(!animate || !A){
8082                 this.dom.style.height = this.addUnits(height);
8083             }else{
8084                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8085             }
8086             return this;
8087         },
8088
8089         /**
8090          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8091          * @param {Number} width The new width
8092          * @param {Number} height The new height
8093          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8094          * @return {Roo.Element} this
8095          */
8096          setSize : function(width, height, animate){
8097             if(typeof width == "object"){ // in case of object from getSize()
8098                 height = width.height; width = width.width;
8099             }
8100             width = this.adjustWidth(width); height = this.adjustHeight(height);
8101             if(!animate || !A){
8102                 this.dom.style.width = this.addUnits(width);
8103                 this.dom.style.height = this.addUnits(height);
8104             }else{
8105                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8106             }
8107             return this;
8108         },
8109
8110         /**
8111          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8112          * @param {Number} x X value for new position (coordinates are page-based)
8113          * @param {Number} y Y value for new position (coordinates are page-based)
8114          * @param {Number} width The new width
8115          * @param {Number} height The new height
8116          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8117          * @return {Roo.Element} this
8118          */
8119         setBounds : function(x, y, width, height, animate){
8120             if(!animate || !A){
8121                 this.setSize(width, height);
8122                 this.setLocation(x, y);
8123             }else{
8124                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8125                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8126                               this.preanim(arguments, 4), 'motion');
8127             }
8128             return this;
8129         },
8130
8131         /**
8132          * 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.
8133          * @param {Roo.lib.Region} region The region to fill
8134          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8135          * @return {Roo.Element} this
8136          */
8137         setRegion : function(region, animate){
8138             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8139             return this;
8140         },
8141
8142         /**
8143          * Appends an event handler
8144          *
8145          * @param {String}   eventName     The type of event to append
8146          * @param {Function} fn        The method the event invokes
8147          * @param {Object} scope       (optional) The scope (this object) of the fn
8148          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8149          */
8150         addListener : function(eventName, fn, scope, options){
8151             if (this.dom) {
8152                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8153             }
8154         },
8155
8156         /**
8157          * Removes an event handler from this element
8158          * @param {String} eventName the type of event to remove
8159          * @param {Function} fn the method the event invokes
8160          * @return {Roo.Element} this
8161          */
8162         removeListener : function(eventName, fn){
8163             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8164             return this;
8165         },
8166
8167         /**
8168          * Removes all previous added listeners from this element
8169          * @return {Roo.Element} this
8170          */
8171         removeAllListeners : function(){
8172             E.purgeElement(this.dom);
8173             return this;
8174         },
8175
8176         relayEvent : function(eventName, observable){
8177             this.on(eventName, function(e){
8178                 observable.fireEvent(eventName, e);
8179             });
8180         },
8181
8182         /**
8183          * Set the opacity of the element
8184          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8185          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8186          * @return {Roo.Element} this
8187          */
8188          setOpacity : function(opacity, animate){
8189             if(!animate || !A){
8190                 var s = this.dom.style;
8191                 if(Roo.isIE){
8192                     s.zoom = 1;
8193                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8194                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8195                 }else{
8196                     s.opacity = opacity;
8197                 }
8198             }else{
8199                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8200             }
8201             return this;
8202         },
8203
8204         /**
8205          * Gets the left X coordinate
8206          * @param {Boolean} local True to get the local css position instead of page coordinate
8207          * @return {Number}
8208          */
8209         getLeft : function(local){
8210             if(!local){
8211                 return this.getX();
8212             }else{
8213                 return parseInt(this.getStyle("left"), 10) || 0;
8214             }
8215         },
8216
8217         /**
8218          * Gets the right X coordinate of the element (element X position + element width)
8219          * @param {Boolean} local True to get the local css position instead of page coordinate
8220          * @return {Number}
8221          */
8222         getRight : function(local){
8223             if(!local){
8224                 return this.getX() + this.getWidth();
8225             }else{
8226                 return (this.getLeft(true) + this.getWidth()) || 0;
8227             }
8228         },
8229
8230         /**
8231          * Gets the top Y coordinate
8232          * @param {Boolean} local True to get the local css position instead of page coordinate
8233          * @return {Number}
8234          */
8235         getTop : function(local) {
8236             if(!local){
8237                 return this.getY();
8238             }else{
8239                 return parseInt(this.getStyle("top"), 10) || 0;
8240             }
8241         },
8242
8243         /**
8244          * Gets the bottom Y coordinate of the element (element Y position + element height)
8245          * @param {Boolean} local True to get the local css position instead of page coordinate
8246          * @return {Number}
8247          */
8248         getBottom : function(local){
8249             if(!local){
8250                 return this.getY() + this.getHeight();
8251             }else{
8252                 return (this.getTop(true) + this.getHeight()) || 0;
8253             }
8254         },
8255
8256         /**
8257         * Initializes positioning on this element. If a desired position is not passed, it will make the
8258         * the element positioned relative IF it is not already positioned.
8259         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8260         * @param {Number} zIndex (optional) The zIndex to apply
8261         * @param {Number} x (optional) Set the page X position
8262         * @param {Number} y (optional) Set the page Y position
8263         */
8264         position : function(pos, zIndex, x, y){
8265             if(!pos){
8266                if(this.getStyle('position') == 'static'){
8267                    this.setStyle('position', 'relative');
8268                }
8269             }else{
8270                 this.setStyle("position", pos);
8271             }
8272             if(zIndex){
8273                 this.setStyle("z-index", zIndex);
8274             }
8275             if(x !== undefined && y !== undefined){
8276                 this.setXY([x, y]);
8277             }else if(x !== undefined){
8278                 this.setX(x);
8279             }else if(y !== undefined){
8280                 this.setY(y);
8281             }
8282         },
8283
8284         /**
8285         * Clear positioning back to the default when the document was loaded
8286         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8287         * @return {Roo.Element} this
8288          */
8289         clearPositioning : function(value){
8290             value = value ||'';
8291             this.setStyle({
8292                 "left": value,
8293                 "right": value,
8294                 "top": value,
8295                 "bottom": value,
8296                 "z-index": "",
8297                 "position" : "static"
8298             });
8299             return this;
8300         },
8301
8302         /**
8303         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8304         * snapshot before performing an update and then restoring the element.
8305         * @return {Object}
8306         */
8307         getPositioning : function(){
8308             var l = this.getStyle("left");
8309             var t = this.getStyle("top");
8310             return {
8311                 "position" : this.getStyle("position"),
8312                 "left" : l,
8313                 "right" : l ? "" : this.getStyle("right"),
8314                 "top" : t,
8315                 "bottom" : t ? "" : this.getStyle("bottom"),
8316                 "z-index" : this.getStyle("z-index")
8317             };
8318         },
8319
8320         /**
8321          * Gets the width of the border(s) for the specified side(s)
8322          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8323          * passing lr would get the border (l)eft width + the border (r)ight width.
8324          * @return {Number} The width of the sides passed added together
8325          */
8326         getBorderWidth : function(side){
8327             return this.addStyles(side, El.borders);
8328         },
8329
8330         /**
8331          * Gets the width of the padding(s) for the specified side(s)
8332          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8333          * passing lr would get the padding (l)eft + the padding (r)ight.
8334          * @return {Number} The padding of the sides passed added together
8335          */
8336         getPadding : function(side){
8337             return this.addStyles(side, El.paddings);
8338         },
8339
8340         /**
8341         * Set positioning with an object returned by getPositioning().
8342         * @param {Object} posCfg
8343         * @return {Roo.Element} this
8344          */
8345         setPositioning : function(pc){
8346             this.applyStyles(pc);
8347             if(pc.right == "auto"){
8348                 this.dom.style.right = "";
8349             }
8350             if(pc.bottom == "auto"){
8351                 this.dom.style.bottom = "";
8352             }
8353             return this;
8354         },
8355
8356         // private
8357         fixDisplay : function(){
8358             if(this.getStyle("display") == "none"){
8359                 this.setStyle("visibility", "hidden");
8360                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8361                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8362                     this.setStyle("display", "block");
8363                 }
8364             }
8365         },
8366
8367         /**
8368          * Quick set left and top adding default units
8369          * @param {String} left The left CSS property value
8370          * @param {String} top The top CSS property value
8371          * @return {Roo.Element} this
8372          */
8373          setLeftTop : function(left, top){
8374             this.dom.style.left = this.addUnits(left);
8375             this.dom.style.top = this.addUnits(top);
8376             return this;
8377         },
8378
8379         /**
8380          * Move this element relative to its current position.
8381          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8382          * @param {Number} distance How far to move the element in pixels
8383          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8384          * @return {Roo.Element} this
8385          */
8386          move : function(direction, distance, animate){
8387             var xy = this.getXY();
8388             direction = direction.toLowerCase();
8389             switch(direction){
8390                 case "l":
8391                 case "left":
8392                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8393                     break;
8394                case "r":
8395                case "right":
8396                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8397                     break;
8398                case "t":
8399                case "top":
8400                case "up":
8401                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8402                     break;
8403                case "b":
8404                case "bottom":
8405                case "down":
8406                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8407                     break;
8408             }
8409             return this;
8410         },
8411
8412         /**
8413          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8414          * @return {Roo.Element} this
8415          */
8416         clip : function(){
8417             if(!this.isClipped){
8418                this.isClipped = true;
8419                this.originalClip = {
8420                    "o": this.getStyle("overflow"),
8421                    "x": this.getStyle("overflow-x"),
8422                    "y": this.getStyle("overflow-y")
8423                };
8424                this.setStyle("overflow", "hidden");
8425                this.setStyle("overflow-x", "hidden");
8426                this.setStyle("overflow-y", "hidden");
8427             }
8428             return this;
8429         },
8430
8431         /**
8432          *  Return clipping (overflow) to original clipping before clip() was called
8433          * @return {Roo.Element} this
8434          */
8435         unclip : function(){
8436             if(this.isClipped){
8437                 this.isClipped = false;
8438                 var o = this.originalClip;
8439                 if(o.o){this.setStyle("overflow", o.o);}
8440                 if(o.x){this.setStyle("overflow-x", o.x);}
8441                 if(o.y){this.setStyle("overflow-y", o.y);}
8442             }
8443             return this;
8444         },
8445
8446
8447         /**
8448          * Gets the x,y coordinates specified by the anchor position on the element.
8449          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8450          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8451          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8452          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8453          * @return {Array} [x, y] An array containing the element's x and y coordinates
8454          */
8455         getAnchorXY : function(anchor, local, s){
8456             //Passing a different size is useful for pre-calculating anchors,
8457             //especially for anchored animations that change the el size.
8458
8459             var w, h, vp = false;
8460             if(!s){
8461                 var d = this.dom;
8462                 if(d == document.body || d == document){
8463                     vp = true;
8464                     w = D.getViewWidth(); h = D.getViewHeight();
8465                 }else{
8466                     w = this.getWidth(); h = this.getHeight();
8467                 }
8468             }else{
8469                 w = s.width;  h = s.height;
8470             }
8471             var x = 0, y = 0, r = Math.round;
8472             switch((anchor || "tl").toLowerCase()){
8473                 case "c":
8474                     x = r(w*.5);
8475                     y = r(h*.5);
8476                 break;
8477                 case "t":
8478                     x = r(w*.5);
8479                     y = 0;
8480                 break;
8481                 case "l":
8482                     x = 0;
8483                     y = r(h*.5);
8484                 break;
8485                 case "r":
8486                     x = w;
8487                     y = r(h*.5);
8488                 break;
8489                 case "b":
8490                     x = r(w*.5);
8491                     y = h;
8492                 break;
8493                 case "tl":
8494                     x = 0;
8495                     y = 0;
8496                 break;
8497                 case "bl":
8498                     x = 0;
8499                     y = h;
8500                 break;
8501                 case "br":
8502                     x = w;
8503                     y = h;
8504                 break;
8505                 case "tr":
8506                     x = w;
8507                     y = 0;
8508                 break;
8509             }
8510             if(local === true){
8511                 return [x, y];
8512             }
8513             if(vp){
8514                 var sc = this.getScroll();
8515                 return [x + sc.left, y + sc.top];
8516             }
8517             //Add the element's offset xy
8518             var o = this.getXY();
8519             return [x+o[0], y+o[1]];
8520         },
8521
8522         /**
8523          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8524          * supported position values.
8525          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8526          * @param {String} position The position to align to.
8527          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8528          * @return {Array} [x, y]
8529          */
8530         getAlignToXY : function(el, p, o){
8531             el = Roo.get(el);
8532             var d = this.dom;
8533             if(!el.dom){
8534                 throw "Element.alignTo with an element that doesn't exist";
8535             }
8536             var c = false; //constrain to viewport
8537             var p1 = "", p2 = "";
8538             o = o || [0,0];
8539
8540             if(!p){
8541                 p = "tl-bl";
8542             }else if(p == "?"){
8543                 p = "tl-bl?";
8544             }else if(p.indexOf("-") == -1){
8545                 p = "tl-" + p;
8546             }
8547             p = p.toLowerCase();
8548             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8549             if(!m){
8550                throw "Element.alignTo with an invalid alignment " + p;
8551             }
8552             p1 = m[1]; p2 = m[2]; c = !!m[3];
8553
8554             //Subtract the aligned el's internal xy from the target's offset xy
8555             //plus custom offset to get the aligned el's new offset xy
8556             var a1 = this.getAnchorXY(p1, true);
8557             var a2 = el.getAnchorXY(p2, false);
8558             var x = a2[0] - a1[0] + o[0];
8559             var y = a2[1] - a1[1] + o[1];
8560             if(c){
8561                 //constrain the aligned el to viewport if necessary
8562                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8563                 // 5px of margin for ie
8564                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8565
8566                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8567                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8568                 //otherwise swap the aligned el to the opposite border of the target.
8569                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8570                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8571                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8572                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8573
8574                var doc = document;
8575                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8576                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8577
8578                if((x+w) > dw + scrollX){
8579                     x = swapX ? r.left-w : dw+scrollX-w;
8580                 }
8581                if(x < scrollX){
8582                    x = swapX ? r.right : scrollX;
8583                }
8584                if((y+h) > dh + scrollY){
8585                     y = swapY ? r.top-h : dh+scrollY-h;
8586                 }
8587                if (y < scrollY){
8588                    y = swapY ? r.bottom : scrollY;
8589                }
8590             }
8591             return [x,y];
8592         },
8593
8594         // private
8595         getConstrainToXY : function(){
8596             var os = {top:0, left:0, bottom:0, right: 0};
8597
8598             return function(el, local, offsets, proposedXY){
8599                 el = Roo.get(el);
8600                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8601
8602                 var vw, vh, vx = 0, vy = 0;
8603                 if(el.dom == document.body || el.dom == document){
8604                     vw = Roo.lib.Dom.getViewWidth();
8605                     vh = Roo.lib.Dom.getViewHeight();
8606                 }else{
8607                     vw = el.dom.clientWidth;
8608                     vh = el.dom.clientHeight;
8609                     if(!local){
8610                         var vxy = el.getXY();
8611                         vx = vxy[0];
8612                         vy = vxy[1];
8613                     }
8614                 }
8615
8616                 var s = el.getScroll();
8617
8618                 vx += offsets.left + s.left;
8619                 vy += offsets.top + s.top;
8620
8621                 vw -= offsets.right;
8622                 vh -= offsets.bottom;
8623
8624                 var vr = vx+vw;
8625                 var vb = vy+vh;
8626
8627                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8628                 var x = xy[0], y = xy[1];
8629                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8630
8631                 // only move it if it needs it
8632                 var moved = false;
8633
8634                 // first validate right/bottom
8635                 if((x + w) > vr){
8636                     x = vr - w;
8637                     moved = true;
8638                 }
8639                 if((y + h) > vb){
8640                     y = vb - h;
8641                     moved = true;
8642                 }
8643                 // then make sure top/left isn't negative
8644                 if(x < vx){
8645                     x = vx;
8646                     moved = true;
8647                 }
8648                 if(y < vy){
8649                     y = vy;
8650                     moved = true;
8651                 }
8652                 return moved ? [x, y] : false;
8653             };
8654         }(),
8655
8656         // private
8657         adjustForConstraints : function(xy, parent, offsets){
8658             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8659         },
8660
8661         /**
8662          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8663          * document it aligns it to the viewport.
8664          * The position parameter is optional, and can be specified in any one of the following formats:
8665          * <ul>
8666          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8667          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8668          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8669          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8670          *   <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
8671          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8672          * </ul>
8673          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8674          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8675          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8676          * that specified in order to enforce the viewport constraints.
8677          * Following are all of the supported anchor positions:
8678     <pre>
8679     Value  Description
8680     -----  -----------------------------
8681     tl     The top left corner (default)
8682     t      The center of the top edge
8683     tr     The top right corner
8684     l      The center of the left edge
8685     c      In the center of the element
8686     r      The center of the right edge
8687     bl     The bottom left corner
8688     b      The center of the bottom edge
8689     br     The bottom right corner
8690     </pre>
8691     Example Usage:
8692     <pre><code>
8693     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8694     el.alignTo("other-el");
8695
8696     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8697     el.alignTo("other-el", "tr?");
8698
8699     // align the bottom right corner of el with the center left edge of other-el
8700     el.alignTo("other-el", "br-l?");
8701
8702     // align the center of el with the bottom left corner of other-el and
8703     // adjust the x position by -6 pixels (and the y position by 0)
8704     el.alignTo("other-el", "c-bl", [-6, 0]);
8705     </code></pre>
8706          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8707          * @param {String} position The position to align to.
8708          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8709          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8710          * @return {Roo.Element} this
8711          */
8712         alignTo : function(element, position, offsets, animate){
8713             var xy = this.getAlignToXY(element, position, offsets);
8714             this.setXY(xy, this.preanim(arguments, 3));
8715             return this;
8716         },
8717
8718         /**
8719          * Anchors an element to another element and realigns it when the window is resized.
8720          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8721          * @param {String} position The position to align to.
8722          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8723          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8724          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8725          * is a number, it is used as the buffer delay (defaults to 50ms).
8726          * @param {Function} callback The function to call after the animation finishes
8727          * @return {Roo.Element} this
8728          */
8729         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8730             var action = function(){
8731                 this.alignTo(el, alignment, offsets, animate);
8732                 Roo.callback(callback, this);
8733             };
8734             Roo.EventManager.onWindowResize(action, this);
8735             var tm = typeof monitorScroll;
8736             if(tm != 'undefined'){
8737                 Roo.EventManager.on(window, 'scroll', action, this,
8738                     {buffer: tm == 'number' ? monitorScroll : 50});
8739             }
8740             action.call(this); // align immediately
8741             return this;
8742         },
8743         /**
8744          * Clears any opacity settings from this element. Required in some cases for IE.
8745          * @return {Roo.Element} this
8746          */
8747         clearOpacity : function(){
8748             if (window.ActiveXObject) {
8749                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8750                     this.dom.style.filter = "";
8751                 }
8752             } else {
8753                 this.dom.style.opacity = "";
8754                 this.dom.style["-moz-opacity"] = "";
8755                 this.dom.style["-khtml-opacity"] = "";
8756             }
8757             return this;
8758         },
8759
8760         /**
8761          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8762          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8763          * @return {Roo.Element} this
8764          */
8765         hide : function(animate){
8766             this.setVisible(false, this.preanim(arguments, 0));
8767             return this;
8768         },
8769
8770         /**
8771         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8772         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8773          * @return {Roo.Element} this
8774          */
8775         show : function(animate){
8776             this.setVisible(true, this.preanim(arguments, 0));
8777             return this;
8778         },
8779
8780         /**
8781          * @private Test if size has a unit, otherwise appends the default
8782          */
8783         addUnits : function(size){
8784             return Roo.Element.addUnits(size, this.defaultUnit);
8785         },
8786
8787         /**
8788          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8789          * @return {Roo.Element} this
8790          */
8791         beginMeasure : function(){
8792             var el = this.dom;
8793             if(el.offsetWidth || el.offsetHeight){
8794                 return this; // offsets work already
8795             }
8796             var changed = [];
8797             var p = this.dom, b = document.body; // start with this element
8798             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8799                 var pe = Roo.get(p);
8800                 if(pe.getStyle('display') == 'none'){
8801                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8802                     p.style.visibility = "hidden";
8803                     p.style.display = "block";
8804                 }
8805                 p = p.parentNode;
8806             }
8807             this._measureChanged = changed;
8808             return this;
8809
8810         },
8811
8812         /**
8813          * Restores displays to before beginMeasure was called
8814          * @return {Roo.Element} this
8815          */
8816         endMeasure : function(){
8817             var changed = this._measureChanged;
8818             if(changed){
8819                 for(var i = 0, len = changed.length; i < len; i++) {
8820                     var r = changed[i];
8821                     r.el.style.visibility = r.visibility;
8822                     r.el.style.display = "none";
8823                 }
8824                 this._measureChanged = null;
8825             }
8826             return this;
8827         },
8828
8829         /**
8830         * Update the innerHTML of this element, optionally searching for and processing scripts
8831         * @param {String} html The new HTML
8832         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8833         * @param {Function} callback For async script loading you can be noticed when the update completes
8834         * @return {Roo.Element} this
8835          */
8836         update : function(html, loadScripts, callback){
8837             if(typeof html == "undefined"){
8838                 html = "";
8839             }
8840             if(loadScripts !== true){
8841                 this.dom.innerHTML = html;
8842                 if(typeof callback == "function"){
8843                     callback();
8844                 }
8845                 return this;
8846             }
8847             var id = Roo.id();
8848             var dom = this.dom;
8849
8850             html += '<span id="' + id + '"></span>';
8851
8852             E.onAvailable(id, function(){
8853                 var hd = document.getElementsByTagName("head")[0];
8854                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8855                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8856                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8857
8858                 var match;
8859                 while(match = re.exec(html)){
8860                     var attrs = match[1];
8861                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8862                     if(srcMatch && srcMatch[2]){
8863                        var s = document.createElement("script");
8864                        s.src = srcMatch[2];
8865                        var typeMatch = attrs.match(typeRe);
8866                        if(typeMatch && typeMatch[2]){
8867                            s.type = typeMatch[2];
8868                        }
8869                        hd.appendChild(s);
8870                     }else if(match[2] && match[2].length > 0){
8871                         if(window.execScript) {
8872                            window.execScript(match[2]);
8873                         } else {
8874                             /**
8875                              * eval:var:id
8876                              * eval:var:dom
8877                              * eval:var:html
8878                              * 
8879                              */
8880                            window.eval(match[2]);
8881                         }
8882                     }
8883                 }
8884                 var el = document.getElementById(id);
8885                 if(el){el.parentNode.removeChild(el);}
8886                 if(typeof callback == "function"){
8887                     callback();
8888                 }
8889             });
8890             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8891             return this;
8892         },
8893
8894         /**
8895          * Direct access to the UpdateManager update() method (takes the same parameters).
8896          * @param {String/Function} url The url for this request or a function to call to get the url
8897          * @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}
8898          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8899          * @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.
8900          * @return {Roo.Element} this
8901          */
8902         load : function(){
8903             var um = this.getUpdateManager();
8904             um.update.apply(um, arguments);
8905             return this;
8906         },
8907
8908         /**
8909         * Gets this element's UpdateManager
8910         * @return {Roo.UpdateManager} The UpdateManager
8911         */
8912         getUpdateManager : function(){
8913             if(!this.updateManager){
8914                 this.updateManager = new Roo.UpdateManager(this);
8915             }
8916             return this.updateManager;
8917         },
8918
8919         /**
8920          * Disables text selection for this element (normalized across browsers)
8921          * @return {Roo.Element} this
8922          */
8923         unselectable : function(){
8924             this.dom.unselectable = "on";
8925             this.swallowEvent("selectstart", true);
8926             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8927             this.addClass("x-unselectable");
8928             return this;
8929         },
8930
8931         /**
8932         * Calculates the x, y to center this element on the screen
8933         * @return {Array} The x, y values [x, y]
8934         */
8935         getCenterXY : function(){
8936             return this.getAlignToXY(document, 'c-c');
8937         },
8938
8939         /**
8940         * Centers the Element in either the viewport, or another Element.
8941         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8942         */
8943         center : function(centerIn){
8944             this.alignTo(centerIn || document, 'c-c');
8945             return this;
8946         },
8947
8948         /**
8949          * Tests various css rules/browsers to determine if this element uses a border box
8950          * @return {Boolean}
8951          */
8952         isBorderBox : function(){
8953             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8954         },
8955
8956         /**
8957          * Return a box {x, y, width, height} that can be used to set another elements
8958          * size/location to match this element.
8959          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8960          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8961          * @return {Object} box An object in the format {x, y, width, height}
8962          */
8963         getBox : function(contentBox, local){
8964             var xy;
8965             if(!local){
8966                 xy = this.getXY();
8967             }else{
8968                 var left = parseInt(this.getStyle("left"), 10) || 0;
8969                 var top = parseInt(this.getStyle("top"), 10) || 0;
8970                 xy = [left, top];
8971             }
8972             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8973             if(!contentBox){
8974                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8975             }else{
8976                 var l = this.getBorderWidth("l")+this.getPadding("l");
8977                 var r = this.getBorderWidth("r")+this.getPadding("r");
8978                 var t = this.getBorderWidth("t")+this.getPadding("t");
8979                 var b = this.getBorderWidth("b")+this.getPadding("b");
8980                 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)};
8981             }
8982             bx.right = bx.x + bx.width;
8983             bx.bottom = bx.y + bx.height;
8984             return bx;
8985         },
8986
8987         /**
8988          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8989          for more information about the sides.
8990          * @param {String} sides
8991          * @return {Number}
8992          */
8993         getFrameWidth : function(sides, onlyContentBox){
8994             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8995         },
8996
8997         /**
8998          * 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.
8999          * @param {Object} box The box to fill {x, y, width, height}
9000          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9001          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9002          * @return {Roo.Element} this
9003          */
9004         setBox : function(box, adjust, animate){
9005             var w = box.width, h = box.height;
9006             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9007                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9008                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9009             }
9010             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9011             return this;
9012         },
9013
9014         /**
9015          * Forces the browser to repaint this element
9016          * @return {Roo.Element} this
9017          */
9018          repaint : function(){
9019             var dom = this.dom;
9020             this.addClass("x-repaint");
9021             setTimeout(function(){
9022                 Roo.get(dom).removeClass("x-repaint");
9023             }, 1);
9024             return this;
9025         },
9026
9027         /**
9028          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9029          * then it returns the calculated width of the sides (see getPadding)
9030          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9031          * @return {Object/Number}
9032          */
9033         getMargins : function(side){
9034             if(!side){
9035                 return {
9036                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9037                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9038                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9039                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9040                 };
9041             }else{
9042                 return this.addStyles(side, El.margins);
9043              }
9044         },
9045
9046         // private
9047         addStyles : function(sides, styles){
9048             var val = 0, v, w;
9049             for(var i = 0, len = sides.length; i < len; i++){
9050                 v = this.getStyle(styles[sides.charAt(i)]);
9051                 if(v){
9052                      w = parseInt(v, 10);
9053                      if(w){ val += w; }
9054                 }
9055             }
9056             return val;
9057         },
9058
9059         /**
9060          * Creates a proxy element of this element
9061          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9062          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9063          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9064          * @return {Roo.Element} The new proxy element
9065          */
9066         createProxy : function(config, renderTo, matchBox){
9067             if(renderTo){
9068                 renderTo = Roo.getDom(renderTo);
9069             }else{
9070                 renderTo = document.body;
9071             }
9072             config = typeof config == "object" ?
9073                 config : {tag : "div", cls: config};
9074             var proxy = Roo.DomHelper.append(renderTo, config, true);
9075             if(matchBox){
9076                proxy.setBox(this.getBox());
9077             }
9078             return proxy;
9079         },
9080
9081         /**
9082          * Puts a mask over this element to disable user interaction. Requires core.css.
9083          * This method can only be applied to elements which accept child nodes.
9084          * @param {String} msg (optional) A message to display in the mask
9085          * @param {String} msgCls (optional) A css class to apply to the msg element
9086          * @return {Element} The mask  element
9087          */
9088         mask : function(msg, msgCls)
9089         {
9090             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9091                 this.setStyle("position", "relative");
9092             }
9093             if(!this._mask){
9094                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9095             }
9096             this.addClass("x-masked");
9097             this._mask.setDisplayed(true);
9098             
9099             // we wander
9100             var z = 0;
9101             var dom = this.dom;
9102             while (dom && dom.style) {
9103                 if (!isNaN(parseInt(dom.style.zIndex))) {
9104                     z = Math.max(z, parseInt(dom.style.zIndex));
9105                 }
9106                 dom = dom.parentNode;
9107             }
9108             // if we are masking the body - then it hides everything..
9109             if (this.dom == document.body) {
9110                 z = 1000000;
9111                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9112                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9113             }
9114            
9115             if(typeof msg == 'string'){
9116                 if(!this._maskMsg){
9117                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9118                 }
9119                 var mm = this._maskMsg;
9120                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9121                 if (mm.dom.firstChild) { // weird IE issue?
9122                     mm.dom.firstChild.innerHTML = msg;
9123                 }
9124                 mm.setDisplayed(true);
9125                 mm.center(this);
9126                 mm.setStyle('z-index', z + 102);
9127             }
9128             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9129                 this._mask.setHeight(this.getHeight());
9130             }
9131             this._mask.setStyle('z-index', z + 100);
9132             
9133             return this._mask;
9134         },
9135
9136         /**
9137          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9138          * it is cached for reuse.
9139          */
9140         unmask : function(removeEl){
9141             if(this._mask){
9142                 if(removeEl === true){
9143                     this._mask.remove();
9144                     delete this._mask;
9145                     if(this._maskMsg){
9146                         this._maskMsg.remove();
9147                         delete this._maskMsg;
9148                     }
9149                 }else{
9150                     this._mask.setDisplayed(false);
9151                     if(this._maskMsg){
9152                         this._maskMsg.setDisplayed(false);
9153                     }
9154                 }
9155             }
9156             this.removeClass("x-masked");
9157         },
9158
9159         /**
9160          * Returns true if this element is masked
9161          * @return {Boolean}
9162          */
9163         isMasked : function(){
9164             return this._mask && this._mask.isVisible();
9165         },
9166
9167         /**
9168          * Creates an iframe shim for this element to keep selects and other windowed objects from
9169          * showing through.
9170          * @return {Roo.Element} The new shim element
9171          */
9172         createShim : function(){
9173             var el = document.createElement('iframe');
9174             el.frameBorder = 'no';
9175             el.className = 'roo-shim';
9176             if(Roo.isIE && Roo.isSecure){
9177                 el.src = Roo.SSL_SECURE_URL;
9178             }
9179             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9180             shim.autoBoxAdjust = false;
9181             return shim;
9182         },
9183
9184         /**
9185          * Removes this element from the DOM and deletes it from the cache
9186          */
9187         remove : function(){
9188             if(this.dom.parentNode){
9189                 this.dom.parentNode.removeChild(this.dom);
9190             }
9191             delete El.cache[this.dom.id];
9192         },
9193
9194         /**
9195          * Sets up event handlers to add and remove a css class when the mouse is over this element
9196          * @param {String} className
9197          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9198          * mouseout events for children elements
9199          * @return {Roo.Element} this
9200          */
9201         addClassOnOver : function(className, preventFlicker){
9202             this.on("mouseover", function(){
9203                 Roo.fly(this, '_internal').addClass(className);
9204             }, this.dom);
9205             var removeFn = function(e){
9206                 if(preventFlicker !== true || !e.within(this, true)){
9207                     Roo.fly(this, '_internal').removeClass(className);
9208                 }
9209             };
9210             this.on("mouseout", removeFn, this.dom);
9211             return this;
9212         },
9213
9214         /**
9215          * Sets up event handlers to add and remove a css class when this element has the focus
9216          * @param {String} className
9217          * @return {Roo.Element} this
9218          */
9219         addClassOnFocus : function(className){
9220             this.on("focus", function(){
9221                 Roo.fly(this, '_internal').addClass(className);
9222             }, this.dom);
9223             this.on("blur", function(){
9224                 Roo.fly(this, '_internal').removeClass(className);
9225             }, this.dom);
9226             return this;
9227         },
9228         /**
9229          * 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)
9230          * @param {String} className
9231          * @return {Roo.Element} this
9232          */
9233         addClassOnClick : function(className){
9234             var dom = this.dom;
9235             this.on("mousedown", function(){
9236                 Roo.fly(dom, '_internal').addClass(className);
9237                 var d = Roo.get(document);
9238                 var fn = function(){
9239                     Roo.fly(dom, '_internal').removeClass(className);
9240                     d.removeListener("mouseup", fn);
9241                 };
9242                 d.on("mouseup", fn);
9243             });
9244             return this;
9245         },
9246
9247         /**
9248          * Stops the specified event from bubbling and optionally prevents the default action
9249          * @param {String} eventName
9250          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9251          * @return {Roo.Element} this
9252          */
9253         swallowEvent : function(eventName, preventDefault){
9254             var fn = function(e){
9255                 e.stopPropagation();
9256                 if(preventDefault){
9257                     e.preventDefault();
9258                 }
9259             };
9260             if(eventName instanceof Array){
9261                 for(var i = 0, len = eventName.length; i < len; i++){
9262                      this.on(eventName[i], fn);
9263                 }
9264                 return this;
9265             }
9266             this.on(eventName, fn);
9267             return this;
9268         },
9269
9270         /**
9271          * @private
9272          */
9273       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9274
9275         /**
9276          * Sizes this element to its parent element's dimensions performing
9277          * neccessary box adjustments.
9278          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9279          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9280          * @return {Roo.Element} this
9281          */
9282         fitToParent : function(monitorResize, targetParent) {
9283           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9284           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9285           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9286             return;
9287           }
9288           var p = Roo.get(targetParent || this.dom.parentNode);
9289           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9290           if (monitorResize === true) {
9291             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9292             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9293           }
9294           return this;
9295         },
9296
9297         /**
9298          * Gets the next sibling, skipping text nodes
9299          * @return {HTMLElement} The next sibling or null
9300          */
9301         getNextSibling : function(){
9302             var n = this.dom.nextSibling;
9303             while(n && n.nodeType != 1){
9304                 n = n.nextSibling;
9305             }
9306             return n;
9307         },
9308
9309         /**
9310          * Gets the previous sibling, skipping text nodes
9311          * @return {HTMLElement} The previous sibling or null
9312          */
9313         getPrevSibling : function(){
9314             var n = this.dom.previousSibling;
9315             while(n && n.nodeType != 1){
9316                 n = n.previousSibling;
9317             }
9318             return n;
9319         },
9320
9321
9322         /**
9323          * Appends the passed element(s) to this element
9324          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9325          * @return {Roo.Element} this
9326          */
9327         appendChild: function(el){
9328             el = Roo.get(el);
9329             el.appendTo(this);
9330             return this;
9331         },
9332
9333         /**
9334          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9335          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9336          * automatically generated with the specified attributes.
9337          * @param {HTMLElement} insertBefore (optional) a child element of this element
9338          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9339          * @return {Roo.Element} The new child element
9340          */
9341         createChild: function(config, insertBefore, returnDom){
9342             config = config || {tag:'div'};
9343             if(insertBefore){
9344                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9345             }
9346             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9347         },
9348
9349         /**
9350          * Appends this element to the passed element
9351          * @param {String/HTMLElement/Element} el The new parent element
9352          * @return {Roo.Element} this
9353          */
9354         appendTo: function(el){
9355             el = Roo.getDom(el);
9356             el.appendChild(this.dom);
9357             return this;
9358         },
9359
9360         /**
9361          * Inserts this element before the passed element in the DOM
9362          * @param {String/HTMLElement/Element} el The element to insert before
9363          * @return {Roo.Element} this
9364          */
9365         insertBefore: function(el){
9366             el = Roo.getDom(el);
9367             el.parentNode.insertBefore(this.dom, el);
9368             return this;
9369         },
9370
9371         /**
9372          * Inserts this element after the passed element in the DOM
9373          * @param {String/HTMLElement/Element} el The element to insert after
9374          * @return {Roo.Element} this
9375          */
9376         insertAfter: function(el){
9377             el = Roo.getDom(el);
9378             el.parentNode.insertBefore(this.dom, el.nextSibling);
9379             return this;
9380         },
9381
9382         /**
9383          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9384          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9385          * @return {Roo.Element} The new child
9386          */
9387         insertFirst: function(el, returnDom){
9388             el = el || {};
9389             if(typeof el == 'object' && !el.nodeType){ // dh config
9390                 return this.createChild(el, this.dom.firstChild, returnDom);
9391             }else{
9392                 el = Roo.getDom(el);
9393                 this.dom.insertBefore(el, this.dom.firstChild);
9394                 return !returnDom ? Roo.get(el) : el;
9395             }
9396         },
9397
9398         /**
9399          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9400          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9401          * @param {String} where (optional) 'before' or 'after' defaults to before
9402          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9403          * @return {Roo.Element} the inserted Element
9404          */
9405         insertSibling: function(el, where, returnDom){
9406             where = where ? where.toLowerCase() : 'before';
9407             el = el || {};
9408             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9409
9410             if(typeof el == 'object' && !el.nodeType){ // dh config
9411                 if(where == 'after' && !this.dom.nextSibling){
9412                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9413                 }else{
9414                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9415                 }
9416
9417             }else{
9418                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9419                             where == 'before' ? this.dom : this.dom.nextSibling);
9420                 if(!returnDom){
9421                     rt = Roo.get(rt);
9422                 }
9423             }
9424             return rt;
9425         },
9426
9427         /**
9428          * Creates and wraps this element with another element
9429          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9430          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9431          * @return {HTMLElement/Element} The newly created wrapper element
9432          */
9433         wrap: function(config, returnDom){
9434             if(!config){
9435                 config = {tag: "div"};
9436             }
9437             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9438             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9439             return newEl;
9440         },
9441
9442         /**
9443          * Replaces the passed element with this element
9444          * @param {String/HTMLElement/Element} el The element to replace
9445          * @return {Roo.Element} this
9446          */
9447         replace: function(el){
9448             el = Roo.get(el);
9449             this.insertBefore(el);
9450             el.remove();
9451             return this;
9452         },
9453
9454         /**
9455          * Inserts an html fragment into this element
9456          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9457          * @param {String} html The HTML fragment
9458          * @param {Boolean} returnEl True to return an Roo.Element
9459          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9460          */
9461         insertHtml : function(where, html, returnEl){
9462             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9463             return returnEl ? Roo.get(el) : el;
9464         },
9465
9466         /**
9467          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9468          * @param {Object} o The object with the attributes
9469          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9470          * @return {Roo.Element} this
9471          */
9472         set : function(o, useSet){
9473             var el = this.dom;
9474             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9475             for(var attr in o){
9476                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9477                 if(attr=="cls"){
9478                     el.className = o["cls"];
9479                 }else{
9480                     if(useSet) {
9481                         el.setAttribute(attr, o[attr]);
9482                     } else {
9483                         el[attr] = o[attr];
9484                     }
9485                 }
9486             }
9487             if(o.style){
9488                 Roo.DomHelper.applyStyles(el, o.style);
9489             }
9490             return this;
9491         },
9492
9493         /**
9494          * Convenience method for constructing a KeyMap
9495          * @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:
9496          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9497          * @param {Function} fn The function to call
9498          * @param {Object} scope (optional) The scope of the function
9499          * @return {Roo.KeyMap} The KeyMap created
9500          */
9501         addKeyListener : function(key, fn, scope){
9502             var config;
9503             if(typeof key != "object" || key instanceof Array){
9504                 config = {
9505                     key: key,
9506                     fn: fn,
9507                     scope: scope
9508                 };
9509             }else{
9510                 config = {
9511                     key : key.key,
9512                     shift : key.shift,
9513                     ctrl : key.ctrl,
9514                     alt : key.alt,
9515                     fn: fn,
9516                     scope: scope
9517                 };
9518             }
9519             return new Roo.KeyMap(this, config);
9520         },
9521
9522         /**
9523          * Creates a KeyMap for this element
9524          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9525          * @return {Roo.KeyMap} The KeyMap created
9526          */
9527         addKeyMap : function(config){
9528             return new Roo.KeyMap(this, config);
9529         },
9530
9531         /**
9532          * Returns true if this element is scrollable.
9533          * @return {Boolean}
9534          */
9535          isScrollable : function(){
9536             var dom = this.dom;
9537             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9538         },
9539
9540         /**
9541          * 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().
9542          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9543          * @param {Number} value The new scroll value
9544          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9545          * @return {Element} this
9546          */
9547
9548         scrollTo : function(side, value, animate){
9549             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9550             if(!animate || !A){
9551                 this.dom[prop] = value;
9552             }else{
9553                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9554                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9555             }
9556             return this;
9557         },
9558
9559         /**
9560          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9561          * within this element's scrollable range.
9562          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9563          * @param {Number} distance How far to scroll the element in pixels
9564          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9565          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9566          * was scrolled as far as it could go.
9567          */
9568          scroll : function(direction, distance, animate){
9569              if(!this.isScrollable()){
9570                  return;
9571              }
9572              var el = this.dom;
9573              var l = el.scrollLeft, t = el.scrollTop;
9574              var w = el.scrollWidth, h = el.scrollHeight;
9575              var cw = el.clientWidth, ch = el.clientHeight;
9576              direction = direction.toLowerCase();
9577              var scrolled = false;
9578              var a = this.preanim(arguments, 2);
9579              switch(direction){
9580                  case "l":
9581                  case "left":
9582                      if(w - l > cw){
9583                          var v = Math.min(l + distance, w-cw);
9584                          this.scrollTo("left", v, a);
9585                          scrolled = true;
9586                      }
9587                      break;
9588                 case "r":
9589                 case "right":
9590                      if(l > 0){
9591                          var v = Math.max(l - distance, 0);
9592                          this.scrollTo("left", v, a);
9593                          scrolled = true;
9594                      }
9595                      break;
9596                 case "t":
9597                 case "top":
9598                 case "up":
9599                      if(t > 0){
9600                          var v = Math.max(t - distance, 0);
9601                          this.scrollTo("top", v, a);
9602                          scrolled = true;
9603                      }
9604                      break;
9605                 case "b":
9606                 case "bottom":
9607                 case "down":
9608                      if(h - t > ch){
9609                          var v = Math.min(t + distance, h-ch);
9610                          this.scrollTo("top", v, a);
9611                          scrolled = true;
9612                      }
9613                      break;
9614              }
9615              return scrolled;
9616         },
9617
9618         /**
9619          * Translates the passed page coordinates into left/top css values for this element
9620          * @param {Number/Array} x The page x or an array containing [x, y]
9621          * @param {Number} y The page y
9622          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9623          */
9624         translatePoints : function(x, y){
9625             if(typeof x == 'object' || x instanceof Array){
9626                 y = x[1]; x = x[0];
9627             }
9628             var p = this.getStyle('position');
9629             var o = this.getXY();
9630
9631             var l = parseInt(this.getStyle('left'), 10);
9632             var t = parseInt(this.getStyle('top'), 10);
9633
9634             if(isNaN(l)){
9635                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9636             }
9637             if(isNaN(t)){
9638                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9639             }
9640
9641             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9642         },
9643
9644         /**
9645          * Returns the current scroll position of the element.
9646          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9647          */
9648         getScroll : function(){
9649             var d = this.dom, doc = document;
9650             if(d == doc || d == doc.body){
9651                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9652                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9653                 return {left: l, top: t};
9654             }else{
9655                 return {left: d.scrollLeft, top: d.scrollTop};
9656             }
9657         },
9658
9659         /**
9660          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9661          * are convert to standard 6 digit hex color.
9662          * @param {String} attr The css attribute
9663          * @param {String} defaultValue The default value to use when a valid color isn't found
9664          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9665          * YUI color anims.
9666          */
9667         getColor : function(attr, defaultValue, prefix){
9668             var v = this.getStyle(attr);
9669             if(!v || v == "transparent" || v == "inherit") {
9670                 return defaultValue;
9671             }
9672             var color = typeof prefix == "undefined" ? "#" : prefix;
9673             if(v.substr(0, 4) == "rgb("){
9674                 var rvs = v.slice(4, v.length -1).split(",");
9675                 for(var i = 0; i < 3; i++){
9676                     var h = parseInt(rvs[i]).toString(16);
9677                     if(h < 16){
9678                         h = "0" + h;
9679                     }
9680                     color += h;
9681                 }
9682             } else {
9683                 if(v.substr(0, 1) == "#"){
9684                     if(v.length == 4) {
9685                         for(var i = 1; i < 4; i++){
9686                             var c = v.charAt(i);
9687                             color +=  c + c;
9688                         }
9689                     }else if(v.length == 7){
9690                         color += v.substr(1);
9691                     }
9692                 }
9693             }
9694             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9695         },
9696
9697         /**
9698          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9699          * gradient background, rounded corners and a 4-way shadow.
9700          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9701          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9702          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9703          * @return {Roo.Element} this
9704          */
9705         boxWrap : function(cls){
9706             cls = cls || 'x-box';
9707             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9708             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9709             return el;
9710         },
9711
9712         /**
9713          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9714          * @param {String} namespace The namespace in which to look for the attribute
9715          * @param {String} name The attribute name
9716          * @return {String} The attribute value
9717          */
9718         getAttributeNS : Roo.isIE ? function(ns, name){
9719             var d = this.dom;
9720             var type = typeof d[ns+":"+name];
9721             if(type != 'undefined' && type != 'unknown'){
9722                 return d[ns+":"+name];
9723             }
9724             return d[name];
9725         } : function(ns, name){
9726             var d = this.dom;
9727             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9728         },
9729         
9730         
9731         /**
9732          * Sets or Returns the value the dom attribute value
9733          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9734          * @param {String} value (optional) The value to set the attribute to
9735          * @return {String} The attribute value
9736          */
9737         attr : function(name){
9738             if (arguments.length > 1) {
9739                 this.dom.setAttribute(name, arguments[1]);
9740                 return arguments[1];
9741             }
9742             if (typeof(name) == 'object') {
9743                 for(var i in name) {
9744                     this.attr(i, name[i]);
9745                 }
9746                 return name;
9747             }
9748             
9749             
9750             if (!this.dom.hasAttribute(name)) {
9751                 return undefined;
9752             }
9753             return this.dom.getAttribute(name);
9754         }
9755         
9756         
9757         
9758     };
9759
9760     var ep = El.prototype;
9761
9762     /**
9763      * Appends an event handler (Shorthand for addListener)
9764      * @param {String}   eventName     The type of event to append
9765      * @param {Function} fn        The method the event invokes
9766      * @param {Object} scope       (optional) The scope (this object) of the fn
9767      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9768      * @method
9769      */
9770     ep.on = ep.addListener;
9771         // backwards compat
9772     ep.mon = ep.addListener;
9773
9774     /**
9775      * Removes an event handler from this element (shorthand for removeListener)
9776      * @param {String} eventName the type of event to remove
9777      * @param {Function} fn the method the event invokes
9778      * @return {Roo.Element} this
9779      * @method
9780      */
9781     ep.un = ep.removeListener;
9782
9783     /**
9784      * true to automatically adjust width and height settings for box-model issues (default to true)
9785      */
9786     ep.autoBoxAdjust = true;
9787
9788     // private
9789     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9790
9791     // private
9792     El.addUnits = function(v, defaultUnit){
9793         if(v === "" || v == "auto"){
9794             return v;
9795         }
9796         if(v === undefined){
9797             return '';
9798         }
9799         if(typeof v == "number" || !El.unitPattern.test(v)){
9800             return v + (defaultUnit || 'px');
9801         }
9802         return v;
9803     };
9804
9805     // special markup used throughout Roo when box wrapping elements
9806     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>';
9807     /**
9808      * Visibility mode constant - Use visibility to hide element
9809      * @static
9810      * @type Number
9811      */
9812     El.VISIBILITY = 1;
9813     /**
9814      * Visibility mode constant - Use display to hide element
9815      * @static
9816      * @type Number
9817      */
9818     El.DISPLAY = 2;
9819
9820     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9821     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9822     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9823
9824
9825
9826     /**
9827      * @private
9828      */
9829     El.cache = {};
9830
9831     var docEl;
9832
9833     /**
9834      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9835      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9836      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9837      * @return {Element} The Element object
9838      * @static
9839      */
9840     El.get = function(el){
9841         var ex, elm, id;
9842         if(!el){ return null; }
9843         if(typeof el == "string"){ // element id
9844             if(!(elm = document.getElementById(el))){
9845                 return null;
9846             }
9847             if(ex = El.cache[el]){
9848                 ex.dom = elm;
9849             }else{
9850                 ex = El.cache[el] = new El(elm);
9851             }
9852             return ex;
9853         }else if(el.tagName){ // dom element
9854             if(!(id = el.id)){
9855                 id = Roo.id(el);
9856             }
9857             if(ex = El.cache[id]){
9858                 ex.dom = el;
9859             }else{
9860                 ex = El.cache[id] = new El(el);
9861             }
9862             return ex;
9863         }else if(el instanceof El){
9864             if(el != docEl){
9865                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9866                                                               // catch case where it hasn't been appended
9867                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9868             }
9869             return el;
9870         }else if(el.isComposite){
9871             return el;
9872         }else if(el instanceof Array){
9873             return El.select(el);
9874         }else if(el == document){
9875             // create a bogus element object representing the document object
9876             if(!docEl){
9877                 var f = function(){};
9878                 f.prototype = El.prototype;
9879                 docEl = new f();
9880                 docEl.dom = document;
9881             }
9882             return docEl;
9883         }
9884         return null;
9885     };
9886
9887     // private
9888     El.uncache = function(el){
9889         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9890             if(a[i]){
9891                 delete El.cache[a[i].id || a[i]];
9892             }
9893         }
9894     };
9895
9896     // private
9897     // Garbage collection - uncache elements/purge listeners on orphaned elements
9898     // so we don't hold a reference and cause the browser to retain them
9899     El.garbageCollect = function(){
9900         if(!Roo.enableGarbageCollector){
9901             clearInterval(El.collectorThread);
9902             return;
9903         }
9904         for(var eid in El.cache){
9905             var el = El.cache[eid], d = el.dom;
9906             // -------------------------------------------------------
9907             // Determining what is garbage:
9908             // -------------------------------------------------------
9909             // !d
9910             // dom node is null, definitely garbage
9911             // -------------------------------------------------------
9912             // !d.parentNode
9913             // no parentNode == direct orphan, definitely garbage
9914             // -------------------------------------------------------
9915             // !d.offsetParent && !document.getElementById(eid)
9916             // display none elements have no offsetParent so we will
9917             // also try to look it up by it's id. However, check
9918             // offsetParent first so we don't do unneeded lookups.
9919             // This enables collection of elements that are not orphans
9920             // directly, but somewhere up the line they have an orphan
9921             // parent.
9922             // -------------------------------------------------------
9923             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9924                 delete El.cache[eid];
9925                 if(d && Roo.enableListenerCollection){
9926                     E.purgeElement(d);
9927                 }
9928             }
9929         }
9930     }
9931     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9932
9933
9934     // dom is optional
9935     El.Flyweight = function(dom){
9936         this.dom = dom;
9937     };
9938     El.Flyweight.prototype = El.prototype;
9939
9940     El._flyweights = {};
9941     /**
9942      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9943      * the dom node can be overwritten by other code.
9944      * @param {String/HTMLElement} el The dom node or id
9945      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9946      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9947      * @static
9948      * @return {Element} The shared Element object
9949      */
9950     El.fly = function(el, named){
9951         named = named || '_global';
9952         el = Roo.getDom(el);
9953         if(!el){
9954             return null;
9955         }
9956         if(!El._flyweights[named]){
9957             El._flyweights[named] = new El.Flyweight();
9958         }
9959         El._flyweights[named].dom = el;
9960         return El._flyweights[named];
9961     };
9962
9963     /**
9964      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9965      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9966      * Shorthand of {@link Roo.Element#get}
9967      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9968      * @return {Element} The Element object
9969      * @member Roo
9970      * @method get
9971      */
9972     Roo.get = El.get;
9973     /**
9974      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9975      * the dom node can be overwritten by other code.
9976      * Shorthand of {@link Roo.Element#fly}
9977      * @param {String/HTMLElement} el The dom node or id
9978      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9979      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9980      * @static
9981      * @return {Element} The shared Element object
9982      * @member Roo
9983      * @method fly
9984      */
9985     Roo.fly = El.fly;
9986
9987     // speedy lookup for elements never to box adjust
9988     var noBoxAdjust = Roo.isStrict ? {
9989         select:1
9990     } : {
9991         input:1, select:1, textarea:1
9992     };
9993     if(Roo.isIE || Roo.isGecko){
9994         noBoxAdjust['button'] = 1;
9995     }
9996
9997
9998     Roo.EventManager.on(window, 'unload', function(){
9999         delete El.cache;
10000         delete El._flyweights;
10001     });
10002 })();
10003
10004
10005
10006
10007 if(Roo.DomQuery){
10008     Roo.Element.selectorFunction = Roo.DomQuery.select;
10009 }
10010
10011 Roo.Element.select = function(selector, unique, root){
10012     var els;
10013     if(typeof selector == "string"){
10014         els = Roo.Element.selectorFunction(selector, root);
10015     }else if(selector.length !== undefined){
10016         els = selector;
10017     }else{
10018         throw "Invalid selector";
10019     }
10020     if(unique === true){
10021         return new Roo.CompositeElement(els);
10022     }else{
10023         return new Roo.CompositeElementLite(els);
10024     }
10025 };
10026 /**
10027  * Selects elements based on the passed CSS selector to enable working on them as 1.
10028  * @param {String/Array} selector The CSS selector or an array of elements
10029  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10030  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10031  * @return {CompositeElementLite/CompositeElement}
10032  * @member Roo
10033  * @method select
10034  */
10035 Roo.select = Roo.Element.select;
10036
10037
10038
10039
10040
10041
10042
10043
10044
10045
10046
10047
10048
10049
10050 /*
10051  * Based on:
10052  * Ext JS Library 1.1.1
10053  * Copyright(c) 2006-2007, Ext JS, LLC.
10054  *
10055  * Originally Released Under LGPL - original licence link has changed is not relivant.
10056  *
10057  * Fork - LGPL
10058  * <script type="text/javascript">
10059  */
10060
10061
10062
10063 //Notifies Element that fx methods are available
10064 Roo.enableFx = true;
10065
10066 /**
10067  * @class Roo.Fx
10068  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10069  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10070  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10071  * Element effects to work.</p><br/>
10072  *
10073  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10074  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10075  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10076  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10077  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10078  * expected results and should be done with care.</p><br/>
10079  *
10080  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10081  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10082 <pre>
10083 Value  Description
10084 -----  -----------------------------
10085 tl     The top left corner
10086 t      The center of the top edge
10087 tr     The top right corner
10088 l      The center of the left edge
10089 r      The center of the right edge
10090 bl     The bottom left corner
10091 b      The center of the bottom edge
10092 br     The bottom right corner
10093 </pre>
10094  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10095  * below are common options that can be passed to any Fx method.</b>
10096  * @cfg {Function} callback A function called when the effect is finished
10097  * @cfg {Object} scope The scope of the effect function
10098  * @cfg {String} easing A valid Easing value for the effect
10099  * @cfg {String} afterCls A css class to apply after the effect
10100  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10101  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10102  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10103  * effects that end with the element being visually hidden, ignored otherwise)
10104  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10105  * a function which returns such a specification that will be applied to the Element after the effect finishes
10106  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10107  * @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
10108  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10109  */
10110 Roo.Fx = {
10111         /**
10112          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10113          * origin for the slide effect.  This function automatically handles wrapping the element with
10114          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10115          * Usage:
10116          *<pre><code>
10117 // default: slide the element in from the top
10118 el.slideIn();
10119
10120 // custom: slide the element in from the right with a 2-second duration
10121 el.slideIn('r', { duration: 2 });
10122
10123 // common config options shown with default values
10124 el.slideIn('t', {
10125     easing: 'easeOut',
10126     duration: .5
10127 });
10128 </code></pre>
10129          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10130          * @param {Object} options (optional) Object literal with any of the Fx config options
10131          * @return {Roo.Element} The Element
10132          */
10133     slideIn : function(anchor, o){
10134         var el = this.getFxEl();
10135         o = o || {};
10136
10137         el.queueFx(o, function(){
10138
10139             anchor = anchor || "t";
10140
10141             // fix display to visibility
10142             this.fixDisplay();
10143
10144             // restore values after effect
10145             var r = this.getFxRestore();
10146             var b = this.getBox();
10147             // fixed size for slide
10148             this.setSize(b);
10149
10150             // wrap if needed
10151             var wrap = this.fxWrap(r.pos, o, "hidden");
10152
10153             var st = this.dom.style;
10154             st.visibility = "visible";
10155             st.position = "absolute";
10156
10157             // clear out temp styles after slide and unwrap
10158             var after = function(){
10159                 el.fxUnwrap(wrap, r.pos, o);
10160                 st.width = r.width;
10161                 st.height = r.height;
10162                 el.afterFx(o);
10163             };
10164             // time to calc the positions
10165             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10166
10167             switch(anchor.toLowerCase()){
10168                 case "t":
10169                     wrap.setSize(b.width, 0);
10170                     st.left = st.bottom = "0";
10171                     a = {height: bh};
10172                 break;
10173                 case "l":
10174                     wrap.setSize(0, b.height);
10175                     st.right = st.top = "0";
10176                     a = {width: bw};
10177                 break;
10178                 case "r":
10179                     wrap.setSize(0, b.height);
10180                     wrap.setX(b.right);
10181                     st.left = st.top = "0";
10182                     a = {width: bw, points: pt};
10183                 break;
10184                 case "b":
10185                     wrap.setSize(b.width, 0);
10186                     wrap.setY(b.bottom);
10187                     st.left = st.top = "0";
10188                     a = {height: bh, points: pt};
10189                 break;
10190                 case "tl":
10191                     wrap.setSize(0, 0);
10192                     st.right = st.bottom = "0";
10193                     a = {width: bw, height: bh};
10194                 break;
10195                 case "bl":
10196                     wrap.setSize(0, 0);
10197                     wrap.setY(b.y+b.height);
10198                     st.right = st.top = "0";
10199                     a = {width: bw, height: bh, points: pt};
10200                 break;
10201                 case "br":
10202                     wrap.setSize(0, 0);
10203                     wrap.setXY([b.right, b.bottom]);
10204                     st.left = st.top = "0";
10205                     a = {width: bw, height: bh, points: pt};
10206                 break;
10207                 case "tr":
10208                     wrap.setSize(0, 0);
10209                     wrap.setX(b.x+b.width);
10210                     st.left = st.bottom = "0";
10211                     a = {width: bw, height: bh, points: pt};
10212                 break;
10213             }
10214             this.dom.style.visibility = "visible";
10215             wrap.show();
10216
10217             arguments.callee.anim = wrap.fxanim(a,
10218                 o,
10219                 'motion',
10220                 .5,
10221                 'easeOut', after);
10222         });
10223         return this;
10224     },
10225     
10226         /**
10227          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10228          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10229          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10230          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10231          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10232          * Usage:
10233          *<pre><code>
10234 // default: slide the element out to the top
10235 el.slideOut();
10236
10237 // custom: slide the element out to the right with a 2-second duration
10238 el.slideOut('r', { duration: 2 });
10239
10240 // common config options shown with default values
10241 el.slideOut('t', {
10242     easing: 'easeOut',
10243     duration: .5,
10244     remove: false,
10245     useDisplay: false
10246 });
10247 </code></pre>
10248          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10249          * @param {Object} options (optional) Object literal with any of the Fx config options
10250          * @return {Roo.Element} The Element
10251          */
10252     slideOut : function(anchor, o){
10253         var el = this.getFxEl();
10254         o = o || {};
10255
10256         el.queueFx(o, function(){
10257
10258             anchor = anchor || "t";
10259
10260             // restore values after effect
10261             var r = this.getFxRestore();
10262             
10263             var b = this.getBox();
10264             // fixed size for slide
10265             this.setSize(b);
10266
10267             // wrap if needed
10268             var wrap = this.fxWrap(r.pos, o, "visible");
10269
10270             var st = this.dom.style;
10271             st.visibility = "visible";
10272             st.position = "absolute";
10273
10274             wrap.setSize(b);
10275
10276             var after = function(){
10277                 if(o.useDisplay){
10278                     el.setDisplayed(false);
10279                 }else{
10280                     el.hide();
10281                 }
10282
10283                 el.fxUnwrap(wrap, r.pos, o);
10284
10285                 st.width = r.width;
10286                 st.height = r.height;
10287
10288                 el.afterFx(o);
10289             };
10290
10291             var a, zero = {to: 0};
10292             switch(anchor.toLowerCase()){
10293                 case "t":
10294                     st.left = st.bottom = "0";
10295                     a = {height: zero};
10296                 break;
10297                 case "l":
10298                     st.right = st.top = "0";
10299                     a = {width: zero};
10300                 break;
10301                 case "r":
10302                     st.left = st.top = "0";
10303                     a = {width: zero, points: {to:[b.right, b.y]}};
10304                 break;
10305                 case "b":
10306                     st.left = st.top = "0";
10307                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10308                 break;
10309                 case "tl":
10310                     st.right = st.bottom = "0";
10311                     a = {width: zero, height: zero};
10312                 break;
10313                 case "bl":
10314                     st.right = st.top = "0";
10315                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10316                 break;
10317                 case "br":
10318                     st.left = st.top = "0";
10319                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10320                 break;
10321                 case "tr":
10322                     st.left = st.bottom = "0";
10323                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10324                 break;
10325             }
10326
10327             arguments.callee.anim = wrap.fxanim(a,
10328                 o,
10329                 'motion',
10330                 .5,
10331                 "easeOut", after);
10332         });
10333         return this;
10334     },
10335
10336         /**
10337          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10338          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10339          * The element must be removed from the DOM using the 'remove' config option if desired.
10340          * Usage:
10341          *<pre><code>
10342 // default
10343 el.puff();
10344
10345 // common config options shown with default values
10346 el.puff({
10347     easing: 'easeOut',
10348     duration: .5,
10349     remove: false,
10350     useDisplay: false
10351 });
10352 </code></pre>
10353          * @param {Object} options (optional) Object literal with any of the Fx config options
10354          * @return {Roo.Element} The Element
10355          */
10356     puff : function(o){
10357         var el = this.getFxEl();
10358         o = o || {};
10359
10360         el.queueFx(o, function(){
10361             this.clearOpacity();
10362             this.show();
10363
10364             // restore values after effect
10365             var r = this.getFxRestore();
10366             var st = this.dom.style;
10367
10368             var after = function(){
10369                 if(o.useDisplay){
10370                     el.setDisplayed(false);
10371                 }else{
10372                     el.hide();
10373                 }
10374
10375                 el.clearOpacity();
10376
10377                 el.setPositioning(r.pos);
10378                 st.width = r.width;
10379                 st.height = r.height;
10380                 st.fontSize = '';
10381                 el.afterFx(o);
10382             };
10383
10384             var width = this.getWidth();
10385             var height = this.getHeight();
10386
10387             arguments.callee.anim = this.fxanim({
10388                     width : {to: this.adjustWidth(width * 2)},
10389                     height : {to: this.adjustHeight(height * 2)},
10390                     points : {by: [-(width * .5), -(height * .5)]},
10391                     opacity : {to: 0},
10392                     fontSize: {to:200, unit: "%"}
10393                 },
10394                 o,
10395                 'motion',
10396                 .5,
10397                 "easeOut", after);
10398         });
10399         return this;
10400     },
10401
10402         /**
10403          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10404          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10405          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10406          * Usage:
10407          *<pre><code>
10408 // default
10409 el.switchOff();
10410
10411 // all config options shown with default values
10412 el.switchOff({
10413     easing: 'easeIn',
10414     duration: .3,
10415     remove: false,
10416     useDisplay: false
10417 });
10418 </code></pre>
10419          * @param {Object} options (optional) Object literal with any of the Fx config options
10420          * @return {Roo.Element} The Element
10421          */
10422     switchOff : function(o){
10423         var el = this.getFxEl();
10424         o = o || {};
10425
10426         el.queueFx(o, function(){
10427             this.clearOpacity();
10428             this.clip();
10429
10430             // restore values after effect
10431             var r = this.getFxRestore();
10432             var st = this.dom.style;
10433
10434             var after = function(){
10435                 if(o.useDisplay){
10436                     el.setDisplayed(false);
10437                 }else{
10438                     el.hide();
10439                 }
10440
10441                 el.clearOpacity();
10442                 el.setPositioning(r.pos);
10443                 st.width = r.width;
10444                 st.height = r.height;
10445
10446                 el.afterFx(o);
10447             };
10448
10449             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10450                 this.clearOpacity();
10451                 (function(){
10452                     this.fxanim({
10453                         height:{to:1},
10454                         points:{by:[0, this.getHeight() * .5]}
10455                     }, o, 'motion', 0.3, 'easeIn', after);
10456                 }).defer(100, this);
10457             });
10458         });
10459         return this;
10460     },
10461
10462     /**
10463      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10464      * changed using the "attr" config option) and then fading back to the original color. If no original
10465      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10466      * Usage:
10467 <pre><code>
10468 // default: highlight background to yellow
10469 el.highlight();
10470
10471 // custom: highlight foreground text to blue for 2 seconds
10472 el.highlight("0000ff", { attr: 'color', duration: 2 });
10473
10474 // common config options shown with default values
10475 el.highlight("ffff9c", {
10476     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10477     endColor: (current color) or "ffffff",
10478     easing: 'easeIn',
10479     duration: 1
10480 });
10481 </code></pre>
10482      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10483      * @param {Object} options (optional) Object literal with any of the Fx config options
10484      * @return {Roo.Element} The Element
10485      */ 
10486     highlight : function(color, o){
10487         var el = this.getFxEl();
10488         o = o || {};
10489
10490         el.queueFx(o, function(){
10491             color = color || "ffff9c";
10492             attr = o.attr || "backgroundColor";
10493
10494             this.clearOpacity();
10495             this.show();
10496
10497             var origColor = this.getColor(attr);
10498             var restoreColor = this.dom.style[attr];
10499             endColor = (o.endColor || origColor) || "ffffff";
10500
10501             var after = function(){
10502                 el.dom.style[attr] = restoreColor;
10503                 el.afterFx(o);
10504             };
10505
10506             var a = {};
10507             a[attr] = {from: color, to: endColor};
10508             arguments.callee.anim = this.fxanim(a,
10509                 o,
10510                 'color',
10511                 1,
10512                 'easeIn', after);
10513         });
10514         return this;
10515     },
10516
10517    /**
10518     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10519     * Usage:
10520 <pre><code>
10521 // default: a single light blue ripple
10522 el.frame();
10523
10524 // custom: 3 red ripples lasting 3 seconds total
10525 el.frame("ff0000", 3, { duration: 3 });
10526
10527 // common config options shown with default values
10528 el.frame("C3DAF9", 1, {
10529     duration: 1 //duration of entire animation (not each individual ripple)
10530     // Note: Easing is not configurable and will be ignored if included
10531 });
10532 </code></pre>
10533     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10534     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10535     * @param {Object} options (optional) Object literal with any of the Fx config options
10536     * @return {Roo.Element} The Element
10537     */
10538     frame : function(color, count, o){
10539         var el = this.getFxEl();
10540         o = o || {};
10541
10542         el.queueFx(o, function(){
10543             color = color || "#C3DAF9";
10544             if(color.length == 6){
10545                 color = "#" + color;
10546             }
10547             count = count || 1;
10548             duration = o.duration || 1;
10549             this.show();
10550
10551             var b = this.getBox();
10552             var animFn = function(){
10553                 var proxy = this.createProxy({
10554
10555                      style:{
10556                         visbility:"hidden",
10557                         position:"absolute",
10558                         "z-index":"35000", // yee haw
10559                         border:"0px solid " + color
10560                      }
10561                   });
10562                 var scale = Roo.isBorderBox ? 2 : 1;
10563                 proxy.animate({
10564                     top:{from:b.y, to:b.y - 20},
10565                     left:{from:b.x, to:b.x - 20},
10566                     borderWidth:{from:0, to:10},
10567                     opacity:{from:1, to:0},
10568                     height:{from:b.height, to:(b.height + (20*scale))},
10569                     width:{from:b.width, to:(b.width + (20*scale))}
10570                 }, duration, function(){
10571                     proxy.remove();
10572                 });
10573                 if(--count > 0){
10574                      animFn.defer((duration/2)*1000, this);
10575                 }else{
10576                     el.afterFx(o);
10577                 }
10578             };
10579             animFn.call(this);
10580         });
10581         return this;
10582     },
10583
10584    /**
10585     * Creates a pause before any subsequent queued effects begin.  If there are
10586     * no effects queued after the pause it will have no effect.
10587     * Usage:
10588 <pre><code>
10589 el.pause(1);
10590 </code></pre>
10591     * @param {Number} seconds The length of time to pause (in seconds)
10592     * @return {Roo.Element} The Element
10593     */
10594     pause : function(seconds){
10595         var el = this.getFxEl();
10596         var o = {};
10597
10598         el.queueFx(o, function(){
10599             setTimeout(function(){
10600                 el.afterFx(o);
10601             }, seconds * 1000);
10602         });
10603         return this;
10604     },
10605
10606    /**
10607     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10608     * using the "endOpacity" config option.
10609     * Usage:
10610 <pre><code>
10611 // default: fade in from opacity 0 to 100%
10612 el.fadeIn();
10613
10614 // custom: fade in from opacity 0 to 75% over 2 seconds
10615 el.fadeIn({ endOpacity: .75, duration: 2});
10616
10617 // common config options shown with default values
10618 el.fadeIn({
10619     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10620     easing: 'easeOut',
10621     duration: .5
10622 });
10623 </code></pre>
10624     * @param {Object} options (optional) Object literal with any of the Fx config options
10625     * @return {Roo.Element} The Element
10626     */
10627     fadeIn : function(o){
10628         var el = this.getFxEl();
10629         o = o || {};
10630         el.queueFx(o, function(){
10631             this.setOpacity(0);
10632             this.fixDisplay();
10633             this.dom.style.visibility = 'visible';
10634             var to = o.endOpacity || 1;
10635             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10636                 o, null, .5, "easeOut", function(){
10637                 if(to == 1){
10638                     this.clearOpacity();
10639                 }
10640                 el.afterFx(o);
10641             });
10642         });
10643         return this;
10644     },
10645
10646    /**
10647     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10648     * using the "endOpacity" config option.
10649     * Usage:
10650 <pre><code>
10651 // default: fade out from the element's current opacity to 0
10652 el.fadeOut();
10653
10654 // custom: fade out from the element's current opacity to 25% over 2 seconds
10655 el.fadeOut({ endOpacity: .25, duration: 2});
10656
10657 // common config options shown with default values
10658 el.fadeOut({
10659     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10660     easing: 'easeOut',
10661     duration: .5
10662     remove: false,
10663     useDisplay: false
10664 });
10665 </code></pre>
10666     * @param {Object} options (optional) Object literal with any of the Fx config options
10667     * @return {Roo.Element} The Element
10668     */
10669     fadeOut : function(o){
10670         var el = this.getFxEl();
10671         o = o || {};
10672         el.queueFx(o, function(){
10673             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10674                 o, null, .5, "easeOut", function(){
10675                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10676                      this.dom.style.display = "none";
10677                 }else{
10678                      this.dom.style.visibility = "hidden";
10679                 }
10680                 this.clearOpacity();
10681                 el.afterFx(o);
10682             });
10683         });
10684         return this;
10685     },
10686
10687    /**
10688     * Animates the transition of an element's dimensions from a starting height/width
10689     * to an ending height/width.
10690     * Usage:
10691 <pre><code>
10692 // change height and width to 100x100 pixels
10693 el.scale(100, 100);
10694
10695 // common config options shown with default values.  The height and width will default to
10696 // the element's existing values if passed as null.
10697 el.scale(
10698     [element's width],
10699     [element's height], {
10700     easing: 'easeOut',
10701     duration: .35
10702 });
10703 </code></pre>
10704     * @param {Number} width  The new width (pass undefined to keep the original width)
10705     * @param {Number} height  The new height (pass undefined to keep the original height)
10706     * @param {Object} options (optional) Object literal with any of the Fx config options
10707     * @return {Roo.Element} The Element
10708     */
10709     scale : function(w, h, o){
10710         this.shift(Roo.apply({}, o, {
10711             width: w,
10712             height: h
10713         }));
10714         return this;
10715     },
10716
10717    /**
10718     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10719     * Any of these properties not specified in the config object will not be changed.  This effect 
10720     * requires that at least one new dimension, position or opacity setting must be passed in on
10721     * the config object in order for the function to have any effect.
10722     * Usage:
10723 <pre><code>
10724 // slide the element horizontally to x position 200 while changing the height and opacity
10725 el.shift({ x: 200, height: 50, opacity: .8 });
10726
10727 // common config options shown with default values.
10728 el.shift({
10729     width: [element's width],
10730     height: [element's height],
10731     x: [element's x position],
10732     y: [element's y position],
10733     opacity: [element's opacity],
10734     easing: 'easeOut',
10735     duration: .35
10736 });
10737 </code></pre>
10738     * @param {Object} options  Object literal with any of the Fx config options
10739     * @return {Roo.Element} The Element
10740     */
10741     shift : function(o){
10742         var el = this.getFxEl();
10743         o = o || {};
10744         el.queueFx(o, function(){
10745             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10746             if(w !== undefined){
10747                 a.width = {to: this.adjustWidth(w)};
10748             }
10749             if(h !== undefined){
10750                 a.height = {to: this.adjustHeight(h)};
10751             }
10752             if(x !== undefined || y !== undefined){
10753                 a.points = {to: [
10754                     x !== undefined ? x : this.getX(),
10755                     y !== undefined ? y : this.getY()
10756                 ]};
10757             }
10758             if(op !== undefined){
10759                 a.opacity = {to: op};
10760             }
10761             if(o.xy !== undefined){
10762                 a.points = {to: o.xy};
10763             }
10764             arguments.callee.anim = this.fxanim(a,
10765                 o, 'motion', .35, "easeOut", function(){
10766                 el.afterFx(o);
10767             });
10768         });
10769         return this;
10770     },
10771
10772         /**
10773          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10774          * ending point of the effect.
10775          * Usage:
10776          *<pre><code>
10777 // default: slide the element downward while fading out
10778 el.ghost();
10779
10780 // custom: slide the element out to the right with a 2-second duration
10781 el.ghost('r', { duration: 2 });
10782
10783 // common config options shown with default values
10784 el.ghost('b', {
10785     easing: 'easeOut',
10786     duration: .5
10787     remove: false,
10788     useDisplay: false
10789 });
10790 </code></pre>
10791          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10792          * @param {Object} options (optional) Object literal with any of the Fx config options
10793          * @return {Roo.Element} The Element
10794          */
10795     ghost : function(anchor, o){
10796         var el = this.getFxEl();
10797         o = o || {};
10798
10799         el.queueFx(o, function(){
10800             anchor = anchor || "b";
10801
10802             // restore values after effect
10803             var r = this.getFxRestore();
10804             var w = this.getWidth(),
10805                 h = this.getHeight();
10806
10807             var st = this.dom.style;
10808
10809             var after = function(){
10810                 if(o.useDisplay){
10811                     el.setDisplayed(false);
10812                 }else{
10813                     el.hide();
10814                 }
10815
10816                 el.clearOpacity();
10817                 el.setPositioning(r.pos);
10818                 st.width = r.width;
10819                 st.height = r.height;
10820
10821                 el.afterFx(o);
10822             };
10823
10824             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10825             switch(anchor.toLowerCase()){
10826                 case "t":
10827                     pt.by = [0, -h];
10828                 break;
10829                 case "l":
10830                     pt.by = [-w, 0];
10831                 break;
10832                 case "r":
10833                     pt.by = [w, 0];
10834                 break;
10835                 case "b":
10836                     pt.by = [0, h];
10837                 break;
10838                 case "tl":
10839                     pt.by = [-w, -h];
10840                 break;
10841                 case "bl":
10842                     pt.by = [-w, h];
10843                 break;
10844                 case "br":
10845                     pt.by = [w, h];
10846                 break;
10847                 case "tr":
10848                     pt.by = [w, -h];
10849                 break;
10850             }
10851
10852             arguments.callee.anim = this.fxanim(a,
10853                 o,
10854                 'motion',
10855                 .5,
10856                 "easeOut", after);
10857         });
10858         return this;
10859     },
10860
10861         /**
10862          * Ensures that all effects queued after syncFx is called on the element are
10863          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10864          * @return {Roo.Element} The Element
10865          */
10866     syncFx : function(){
10867         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10868             block : false,
10869             concurrent : true,
10870             stopFx : false
10871         });
10872         return this;
10873     },
10874
10875         /**
10876          * Ensures that all effects queued after sequenceFx is called on the element are
10877          * run in sequence.  This is the opposite of {@link #syncFx}.
10878          * @return {Roo.Element} The Element
10879          */
10880     sequenceFx : function(){
10881         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10882             block : false,
10883             concurrent : false,
10884             stopFx : false
10885         });
10886         return this;
10887     },
10888
10889         /* @private */
10890     nextFx : function(){
10891         var ef = this.fxQueue[0];
10892         if(ef){
10893             ef.call(this);
10894         }
10895     },
10896
10897         /**
10898          * Returns true if the element has any effects actively running or queued, else returns false.
10899          * @return {Boolean} True if element has active effects, else false
10900          */
10901     hasActiveFx : function(){
10902         return this.fxQueue && this.fxQueue[0];
10903     },
10904
10905         /**
10906          * Stops any running effects and clears the element's internal effects queue if it contains
10907          * any additional effects that haven't started yet.
10908          * @return {Roo.Element} The Element
10909          */
10910     stopFx : function(){
10911         if(this.hasActiveFx()){
10912             var cur = this.fxQueue[0];
10913             if(cur && cur.anim && cur.anim.isAnimated()){
10914                 this.fxQueue = [cur]; // clear out others
10915                 cur.anim.stop(true);
10916             }
10917         }
10918         return this;
10919     },
10920
10921         /* @private */
10922     beforeFx : function(o){
10923         if(this.hasActiveFx() && !o.concurrent){
10924            if(o.stopFx){
10925                this.stopFx();
10926                return true;
10927            }
10928            return false;
10929         }
10930         return true;
10931     },
10932
10933         /**
10934          * Returns true if the element is currently blocking so that no other effect can be queued
10935          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10936          * used to ensure that an effect initiated by a user action runs to completion prior to the
10937          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10938          * @return {Boolean} True if blocking, else false
10939          */
10940     hasFxBlock : function(){
10941         var q = this.fxQueue;
10942         return q && q[0] && q[0].block;
10943     },
10944
10945         /* @private */
10946     queueFx : function(o, fn){
10947         if(!this.fxQueue){
10948             this.fxQueue = [];
10949         }
10950         if(!this.hasFxBlock()){
10951             Roo.applyIf(o, this.fxDefaults);
10952             if(!o.concurrent){
10953                 var run = this.beforeFx(o);
10954                 fn.block = o.block;
10955                 this.fxQueue.push(fn);
10956                 if(run){
10957                     this.nextFx();
10958                 }
10959             }else{
10960                 fn.call(this);
10961             }
10962         }
10963         return this;
10964     },
10965
10966         /* @private */
10967     fxWrap : function(pos, o, vis){
10968         var wrap;
10969         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10970             var wrapXY;
10971             if(o.fixPosition){
10972                 wrapXY = this.getXY();
10973             }
10974             var div = document.createElement("div");
10975             div.style.visibility = vis;
10976             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10977             wrap.setPositioning(pos);
10978             if(wrap.getStyle("position") == "static"){
10979                 wrap.position("relative");
10980             }
10981             this.clearPositioning('auto');
10982             wrap.clip();
10983             wrap.dom.appendChild(this.dom);
10984             if(wrapXY){
10985                 wrap.setXY(wrapXY);
10986             }
10987         }
10988         return wrap;
10989     },
10990
10991         /* @private */
10992     fxUnwrap : function(wrap, pos, o){
10993         this.clearPositioning();
10994         this.setPositioning(pos);
10995         if(!o.wrap){
10996             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10997             wrap.remove();
10998         }
10999     },
11000
11001         /* @private */
11002     getFxRestore : function(){
11003         var st = this.dom.style;
11004         return {pos: this.getPositioning(), width: st.width, height : st.height};
11005     },
11006
11007         /* @private */
11008     afterFx : function(o){
11009         if(o.afterStyle){
11010             this.applyStyles(o.afterStyle);
11011         }
11012         if(o.afterCls){
11013             this.addClass(o.afterCls);
11014         }
11015         if(o.remove === true){
11016             this.remove();
11017         }
11018         Roo.callback(o.callback, o.scope, [this]);
11019         if(!o.concurrent){
11020             this.fxQueue.shift();
11021             this.nextFx();
11022         }
11023     },
11024
11025         /* @private */
11026     getFxEl : function(){ // support for composite element fx
11027         return Roo.get(this.dom);
11028     },
11029
11030         /* @private */
11031     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11032         animType = animType || 'run';
11033         opt = opt || {};
11034         var anim = Roo.lib.Anim[animType](
11035             this.dom, args,
11036             (opt.duration || defaultDur) || .35,
11037             (opt.easing || defaultEase) || 'easeOut',
11038             function(){
11039                 Roo.callback(cb, this);
11040             },
11041             this
11042         );
11043         opt.anim = anim;
11044         return anim;
11045     }
11046 };
11047
11048 // backwords compat
11049 Roo.Fx.resize = Roo.Fx.scale;
11050
11051 //When included, Roo.Fx is automatically applied to Element so that all basic
11052 //effects are available directly via the Element API
11053 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11054  * Based on:
11055  * Ext JS Library 1.1.1
11056  * Copyright(c) 2006-2007, Ext JS, LLC.
11057  *
11058  * Originally Released Under LGPL - original licence link has changed is not relivant.
11059  *
11060  * Fork - LGPL
11061  * <script type="text/javascript">
11062  */
11063
11064
11065 /**
11066  * @class Roo.CompositeElement
11067  * Standard composite class. Creates a Roo.Element for every element in the collection.
11068  * <br><br>
11069  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11070  * actions will be performed on all the elements in this collection.</b>
11071  * <br><br>
11072  * All methods return <i>this</i> and can be chained.
11073  <pre><code>
11074  var els = Roo.select("#some-el div.some-class", true);
11075  // or select directly from an existing element
11076  var el = Roo.get('some-el');
11077  el.select('div.some-class', true);
11078
11079  els.setWidth(100); // all elements become 100 width
11080  els.hide(true); // all elements fade out and hide
11081  // or
11082  els.setWidth(100).hide(true);
11083  </code></pre>
11084  */
11085 Roo.CompositeElement = function(els){
11086     this.elements = [];
11087     this.addElements(els);
11088 };
11089 Roo.CompositeElement.prototype = {
11090     isComposite: true,
11091     addElements : function(els){
11092         if(!els) {
11093             return this;
11094         }
11095         if(typeof els == "string"){
11096             els = Roo.Element.selectorFunction(els);
11097         }
11098         var yels = this.elements;
11099         var index = yels.length-1;
11100         for(var i = 0, len = els.length; i < len; i++) {
11101                 yels[++index] = Roo.get(els[i]);
11102         }
11103         return this;
11104     },
11105
11106     /**
11107     * Clears this composite and adds the elements returned by the passed selector.
11108     * @param {String/Array} els A string CSS selector, an array of elements or an element
11109     * @return {CompositeElement} this
11110     */
11111     fill : function(els){
11112         this.elements = [];
11113         this.add(els);
11114         return this;
11115     },
11116
11117     /**
11118     * Filters this composite to only elements that match the passed selector.
11119     * @param {String} selector A string CSS selector
11120     * @param {Boolean} inverse return inverse filter (not matches)
11121     * @return {CompositeElement} this
11122     */
11123     filter : function(selector, inverse){
11124         var els = [];
11125         inverse = inverse || false;
11126         this.each(function(el){
11127             var match = inverse ? !el.is(selector) : el.is(selector);
11128             if(match){
11129                 els[els.length] = el.dom;
11130             }
11131         });
11132         this.fill(els);
11133         return this;
11134     },
11135
11136     invoke : function(fn, args){
11137         var els = this.elements;
11138         for(var i = 0, len = els.length; i < len; i++) {
11139                 Roo.Element.prototype[fn].apply(els[i], args);
11140         }
11141         return this;
11142     },
11143     /**
11144     * Adds elements to this composite.
11145     * @param {String/Array} els A string CSS selector, an array of elements or an element
11146     * @return {CompositeElement} this
11147     */
11148     add : function(els){
11149         if(typeof els == "string"){
11150             this.addElements(Roo.Element.selectorFunction(els));
11151         }else if(els.length !== undefined){
11152             this.addElements(els);
11153         }else{
11154             this.addElements([els]);
11155         }
11156         return this;
11157     },
11158     /**
11159     * Calls the passed function passing (el, this, index) for each element in this composite.
11160     * @param {Function} fn The function to call
11161     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11162     * @return {CompositeElement} this
11163     */
11164     each : function(fn, scope){
11165         var els = this.elements;
11166         for(var i = 0, len = els.length; i < len; i++){
11167             if(fn.call(scope || els[i], els[i], this, i) === false) {
11168                 break;
11169             }
11170         }
11171         return this;
11172     },
11173
11174     /**
11175      * Returns the Element object at the specified index
11176      * @param {Number} index
11177      * @return {Roo.Element}
11178      */
11179     item : function(index){
11180         return this.elements[index] || null;
11181     },
11182
11183     /**
11184      * Returns the first Element
11185      * @return {Roo.Element}
11186      */
11187     first : function(){
11188         return this.item(0);
11189     },
11190
11191     /**
11192      * Returns the last Element
11193      * @return {Roo.Element}
11194      */
11195     last : function(){
11196         return this.item(this.elements.length-1);
11197     },
11198
11199     /**
11200      * Returns the number of elements in this composite
11201      * @return Number
11202      */
11203     getCount : function(){
11204         return this.elements.length;
11205     },
11206
11207     /**
11208      * Returns true if this composite contains the passed element
11209      * @return Boolean
11210      */
11211     contains : function(el){
11212         return this.indexOf(el) !== -1;
11213     },
11214
11215     /**
11216      * Returns true if this composite contains the passed element
11217      * @return Boolean
11218      */
11219     indexOf : function(el){
11220         return this.elements.indexOf(Roo.get(el));
11221     },
11222
11223
11224     /**
11225     * Removes the specified element(s).
11226     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11227     * or an array of any of those.
11228     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11229     * @return {CompositeElement} this
11230     */
11231     removeElement : function(el, removeDom){
11232         if(el instanceof Array){
11233             for(var i = 0, len = el.length; i < len; i++){
11234                 this.removeElement(el[i]);
11235             }
11236             return this;
11237         }
11238         var index = typeof el == 'number' ? el : this.indexOf(el);
11239         if(index !== -1){
11240             if(removeDom){
11241                 var d = this.elements[index];
11242                 if(d.dom){
11243                     d.remove();
11244                 }else{
11245                     d.parentNode.removeChild(d);
11246                 }
11247             }
11248             this.elements.splice(index, 1);
11249         }
11250         return this;
11251     },
11252
11253     /**
11254     * Replaces the specified element with the passed element.
11255     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11256     * to replace.
11257     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11258     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11259     * @return {CompositeElement} this
11260     */
11261     replaceElement : function(el, replacement, domReplace){
11262         var index = typeof el == 'number' ? el : this.indexOf(el);
11263         if(index !== -1){
11264             if(domReplace){
11265                 this.elements[index].replaceWith(replacement);
11266             }else{
11267                 this.elements.splice(index, 1, Roo.get(replacement))
11268             }
11269         }
11270         return this;
11271     },
11272
11273     /**
11274      * Removes all elements.
11275      */
11276     clear : function(){
11277         this.elements = [];
11278     }
11279 };
11280 (function(){
11281     Roo.CompositeElement.createCall = function(proto, fnName){
11282         if(!proto[fnName]){
11283             proto[fnName] = function(){
11284                 return this.invoke(fnName, arguments);
11285             };
11286         }
11287     };
11288     for(var fnName in Roo.Element.prototype){
11289         if(typeof Roo.Element.prototype[fnName] == "function"){
11290             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11291         }
11292     };
11293 })();
11294 /*
11295  * Based on:
11296  * Ext JS Library 1.1.1
11297  * Copyright(c) 2006-2007, Ext JS, LLC.
11298  *
11299  * Originally Released Under LGPL - original licence link has changed is not relivant.
11300  *
11301  * Fork - LGPL
11302  * <script type="text/javascript">
11303  */
11304
11305 /**
11306  * @class Roo.CompositeElementLite
11307  * @extends Roo.CompositeElement
11308  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11309  <pre><code>
11310  var els = Roo.select("#some-el div.some-class");
11311  // or select directly from an existing element
11312  var el = Roo.get('some-el');
11313  el.select('div.some-class');
11314
11315  els.setWidth(100); // all elements become 100 width
11316  els.hide(true); // all elements fade out and hide
11317  // or
11318  els.setWidth(100).hide(true);
11319  </code></pre><br><br>
11320  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11321  * actions will be performed on all the elements in this collection.</b>
11322  */
11323 Roo.CompositeElementLite = function(els){
11324     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11325     this.el = new Roo.Element.Flyweight();
11326 };
11327 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11328     addElements : function(els){
11329         if(els){
11330             if(els instanceof Array){
11331                 this.elements = this.elements.concat(els);
11332             }else{
11333                 var yels = this.elements;
11334                 var index = yels.length-1;
11335                 for(var i = 0, len = els.length; i < len; i++) {
11336                     yels[++index] = els[i];
11337                 }
11338             }
11339         }
11340         return this;
11341     },
11342     invoke : function(fn, args){
11343         var els = this.elements;
11344         var el = this.el;
11345         for(var i = 0, len = els.length; i < len; i++) {
11346             el.dom = els[i];
11347                 Roo.Element.prototype[fn].apply(el, args);
11348         }
11349         return this;
11350     },
11351     /**
11352      * Returns a flyweight Element of the dom element object at the specified index
11353      * @param {Number} index
11354      * @return {Roo.Element}
11355      */
11356     item : function(index){
11357         if(!this.elements[index]){
11358             return null;
11359         }
11360         this.el.dom = this.elements[index];
11361         return this.el;
11362     },
11363
11364     // fixes scope with flyweight
11365     addListener : function(eventName, handler, scope, opt){
11366         var els = this.elements;
11367         for(var i = 0, len = els.length; i < len; i++) {
11368             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11369         }
11370         return this;
11371     },
11372
11373     /**
11374     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11375     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11376     * a reference to the dom node, use el.dom.</b>
11377     * @param {Function} fn The function to call
11378     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11379     * @return {CompositeElement} this
11380     */
11381     each : function(fn, scope){
11382         var els = this.elements;
11383         var el = this.el;
11384         for(var i = 0, len = els.length; i < len; i++){
11385             el.dom = els[i];
11386                 if(fn.call(scope || el, el, this, i) === false){
11387                 break;
11388             }
11389         }
11390         return this;
11391     },
11392
11393     indexOf : function(el){
11394         return this.elements.indexOf(Roo.getDom(el));
11395     },
11396
11397     replaceElement : function(el, replacement, domReplace){
11398         var index = typeof el == 'number' ? el : this.indexOf(el);
11399         if(index !== -1){
11400             replacement = Roo.getDom(replacement);
11401             if(domReplace){
11402                 var d = this.elements[index];
11403                 d.parentNode.insertBefore(replacement, d);
11404                 d.parentNode.removeChild(d);
11405             }
11406             this.elements.splice(index, 1, replacement);
11407         }
11408         return this;
11409     }
11410 });
11411 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11412
11413 /*
11414  * Based on:
11415  * Ext JS Library 1.1.1
11416  * Copyright(c) 2006-2007, Ext JS, LLC.
11417  *
11418  * Originally Released Under LGPL - original licence link has changed is not relivant.
11419  *
11420  * Fork - LGPL
11421  * <script type="text/javascript">
11422  */
11423
11424  
11425
11426 /**
11427  * @class Roo.data.Connection
11428  * @extends Roo.util.Observable
11429  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11430  * either to a configured URL, or to a URL specified at request time.<br><br>
11431  * <p>
11432  * Requests made by this class are asynchronous, and will return immediately. No data from
11433  * the server will be available to the statement immediately following the {@link #request} call.
11434  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11435  * <p>
11436  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11437  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11438  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11439  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11440  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11441  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11442  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11443  * standard DOM methods.
11444  * @constructor
11445  * @param {Object} config a configuration object.
11446  */
11447 Roo.data.Connection = function(config){
11448     Roo.apply(this, config);
11449     this.addEvents({
11450         /**
11451          * @event beforerequest
11452          * Fires before a network request is made to retrieve a data object.
11453          * @param {Connection} conn This Connection object.
11454          * @param {Object} options The options config object passed to the {@link #request} method.
11455          */
11456         "beforerequest" : true,
11457         /**
11458          * @event requestcomplete
11459          * Fires if the request was successfully completed.
11460          * @param {Connection} conn This Connection object.
11461          * @param {Object} response The XHR object containing the response data.
11462          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11463          * @param {Object} options The options config object passed to the {@link #request} method.
11464          */
11465         "requestcomplete" : true,
11466         /**
11467          * @event requestexception
11468          * Fires if an error HTTP status was returned from the server.
11469          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11470          * @param {Connection} conn This Connection object.
11471          * @param {Object} response The XHR object containing the response data.
11472          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11473          * @param {Object} options The options config object passed to the {@link #request} method.
11474          */
11475         "requestexception" : true
11476     });
11477     Roo.data.Connection.superclass.constructor.call(this);
11478 };
11479
11480 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11481     /**
11482      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11483      */
11484     /**
11485      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11486      * extra parameters to each request made by this object. (defaults to undefined)
11487      */
11488     /**
11489      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11490      *  to each request made by this object. (defaults to undefined)
11491      */
11492     /**
11493      * @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)
11494      */
11495     /**
11496      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11497      */
11498     timeout : 30000,
11499     /**
11500      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11501      * @type Boolean
11502      */
11503     autoAbort:false,
11504
11505     /**
11506      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11507      * @type Boolean
11508      */
11509     disableCaching: true,
11510
11511     /**
11512      * Sends an HTTP request to a remote server.
11513      * @param {Object} options An object which may contain the following properties:<ul>
11514      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11515      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11516      * request, a url encoded string or a function to call to get either.</li>
11517      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11518      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11519      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11520      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11521      * <li>options {Object} The parameter to the request call.</li>
11522      * <li>success {Boolean} True if the request succeeded.</li>
11523      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11524      * </ul></li>
11525      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11526      * The callback is passed the following parameters:<ul>
11527      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11528      * <li>options {Object} The parameter to the request call.</li>
11529      * </ul></li>
11530      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11531      * The callback is passed the following parameters:<ul>
11532      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11533      * <li>options {Object} The parameter to the request call.</li>
11534      * </ul></li>
11535      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11536      * for the callback function. Defaults to the browser window.</li>
11537      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11538      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11539      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11540      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11541      * params for the post data. Any params will be appended to the URL.</li>
11542      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11543      * </ul>
11544      * @return {Number} transactionId
11545      */
11546     request : function(o){
11547         if(this.fireEvent("beforerequest", this, o) !== false){
11548             var p = o.params;
11549
11550             if(typeof p == "function"){
11551                 p = p.call(o.scope||window, o);
11552             }
11553             if(typeof p == "object"){
11554                 p = Roo.urlEncode(o.params);
11555             }
11556             if(this.extraParams){
11557                 var extras = Roo.urlEncode(this.extraParams);
11558                 p = p ? (p + '&' + extras) : extras;
11559             }
11560
11561             var url = o.url || this.url;
11562             if(typeof url == 'function'){
11563                 url = url.call(o.scope||window, o);
11564             }
11565
11566             if(o.form){
11567                 var form = Roo.getDom(o.form);
11568                 url = url || form.action;
11569
11570                 var enctype = form.getAttribute("enctype");
11571                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11572                     return this.doFormUpload(o, p, url);
11573                 }
11574                 var f = Roo.lib.Ajax.serializeForm(form);
11575                 p = p ? (p + '&' + f) : f;
11576             }
11577
11578             var hs = o.headers;
11579             if(this.defaultHeaders){
11580                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11581                 if(!o.headers){
11582                     o.headers = hs;
11583                 }
11584             }
11585
11586             var cb = {
11587                 success: this.handleResponse,
11588                 failure: this.handleFailure,
11589                 scope: this,
11590                 argument: {options: o},
11591                 timeout : o.timeout || this.timeout
11592             };
11593
11594             var method = o.method||this.method||(p ? "POST" : "GET");
11595
11596             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11597                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11598             }
11599
11600             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11601                 if(o.autoAbort){
11602                     this.abort();
11603                 }
11604             }else if(this.autoAbort !== false){
11605                 this.abort();
11606             }
11607
11608             if((method == 'GET' && p) || o.xmlData){
11609                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11610                 p = '';
11611             }
11612             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11613             return this.transId;
11614         }else{
11615             Roo.callback(o.callback, o.scope, [o, null, null]);
11616             return null;
11617         }
11618     },
11619
11620     /**
11621      * Determine whether this object has a request outstanding.
11622      * @param {Number} transactionId (Optional) defaults to the last transaction
11623      * @return {Boolean} True if there is an outstanding request.
11624      */
11625     isLoading : function(transId){
11626         if(transId){
11627             return Roo.lib.Ajax.isCallInProgress(transId);
11628         }else{
11629             return this.transId ? true : false;
11630         }
11631     },
11632
11633     /**
11634      * Aborts any outstanding request.
11635      * @param {Number} transactionId (Optional) defaults to the last transaction
11636      */
11637     abort : function(transId){
11638         if(transId || this.isLoading()){
11639             Roo.lib.Ajax.abort(transId || this.transId);
11640         }
11641     },
11642
11643     // private
11644     handleResponse : function(response){
11645         this.transId = false;
11646         var options = response.argument.options;
11647         response.argument = options ? options.argument : null;
11648         this.fireEvent("requestcomplete", this, response, options);
11649         Roo.callback(options.success, options.scope, [response, options]);
11650         Roo.callback(options.callback, options.scope, [options, true, response]);
11651     },
11652
11653     // private
11654     handleFailure : function(response, e){
11655         this.transId = false;
11656         var options = response.argument.options;
11657         response.argument = options ? options.argument : null;
11658         this.fireEvent("requestexception", this, response, options, e);
11659         Roo.callback(options.failure, options.scope, [response, options]);
11660         Roo.callback(options.callback, options.scope, [options, false, response]);
11661     },
11662
11663     // private
11664     doFormUpload : function(o, ps, url){
11665         var id = Roo.id();
11666         var frame = document.createElement('iframe');
11667         frame.id = id;
11668         frame.name = id;
11669         frame.className = 'x-hidden';
11670         if(Roo.isIE){
11671             frame.src = Roo.SSL_SECURE_URL;
11672         }
11673         document.body.appendChild(frame);
11674
11675         if(Roo.isIE){
11676            document.frames[id].name = id;
11677         }
11678
11679         var form = Roo.getDom(o.form);
11680         form.target = id;
11681         form.method = 'POST';
11682         form.enctype = form.encoding = 'multipart/form-data';
11683         if(url){
11684             form.action = url;
11685         }
11686
11687         var hiddens, hd;
11688         if(ps){ // add dynamic params
11689             hiddens = [];
11690             ps = Roo.urlDecode(ps, false);
11691             for(var k in ps){
11692                 if(ps.hasOwnProperty(k)){
11693                     hd = document.createElement('input');
11694                     hd.type = 'hidden';
11695                     hd.name = k;
11696                     hd.value = ps[k];
11697                     form.appendChild(hd);
11698                     hiddens.push(hd);
11699                 }
11700             }
11701         }
11702
11703         function cb(){
11704             var r = {  // bogus response object
11705                 responseText : '',
11706                 responseXML : null
11707             };
11708
11709             r.argument = o ? o.argument : null;
11710
11711             try { //
11712                 var doc;
11713                 if(Roo.isIE){
11714                     doc = frame.contentWindow.document;
11715                 }else {
11716                     doc = (frame.contentDocument || window.frames[id].document);
11717                 }
11718                 if(doc && doc.body){
11719                     r.responseText = doc.body.innerHTML;
11720                 }
11721                 if(doc && doc.XMLDocument){
11722                     r.responseXML = doc.XMLDocument;
11723                 }else {
11724                     r.responseXML = doc;
11725                 }
11726             }
11727             catch(e) {
11728                 // ignore
11729             }
11730
11731             Roo.EventManager.removeListener(frame, 'load', cb, this);
11732
11733             this.fireEvent("requestcomplete", this, r, o);
11734             Roo.callback(o.success, o.scope, [r, o]);
11735             Roo.callback(o.callback, o.scope, [o, true, r]);
11736
11737             setTimeout(function(){document.body.removeChild(frame);}, 100);
11738         }
11739
11740         Roo.EventManager.on(frame, 'load', cb, this);
11741         form.submit();
11742
11743         if(hiddens){ // remove dynamic params
11744             for(var i = 0, len = hiddens.length; i < len; i++){
11745                 form.removeChild(hiddens[i]);
11746             }
11747         }
11748     }
11749 });
11750 /*
11751  * Based on:
11752  * Ext JS Library 1.1.1
11753  * Copyright(c) 2006-2007, Ext JS, LLC.
11754  *
11755  * Originally Released Under LGPL - original licence link has changed is not relivant.
11756  *
11757  * Fork - LGPL
11758  * <script type="text/javascript">
11759  */
11760  
11761 /**
11762  * Global Ajax request class.
11763  * 
11764  * @class Roo.Ajax
11765  * @extends Roo.data.Connection
11766  * @static
11767  * 
11768  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11769  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11770  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11771  * @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)
11772  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11773  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11774  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11775  */
11776 Roo.Ajax = new Roo.data.Connection({
11777     // fix up the docs
11778     /**
11779      * @scope Roo.Ajax
11780      * @type {Boolear} 
11781      */
11782     autoAbort : false,
11783
11784     /**
11785      * Serialize the passed form into a url encoded string
11786      * @scope Roo.Ajax
11787      * @param {String/HTMLElement} form
11788      * @return {String}
11789      */
11790     serializeForm : function(form){
11791         return Roo.lib.Ajax.serializeForm(form);
11792     }
11793 });/*
11794  * Based on:
11795  * Ext JS Library 1.1.1
11796  * Copyright(c) 2006-2007, Ext JS, LLC.
11797  *
11798  * Originally Released Under LGPL - original licence link has changed is not relivant.
11799  *
11800  * Fork - LGPL
11801  * <script type="text/javascript">
11802  */
11803
11804  
11805 /**
11806  * @class Roo.UpdateManager
11807  * @extends Roo.util.Observable
11808  * Provides AJAX-style update for Element object.<br><br>
11809  * Usage:<br>
11810  * <pre><code>
11811  * // Get it from a Roo.Element object
11812  * var el = Roo.get("foo");
11813  * var mgr = el.getUpdateManager();
11814  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11815  * ...
11816  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11817  * <br>
11818  * // or directly (returns the same UpdateManager instance)
11819  * var mgr = new Roo.UpdateManager("myElementId");
11820  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11821  * mgr.on("update", myFcnNeedsToKnow);
11822  * <br>
11823    // short handed call directly from the element object
11824    Roo.get("foo").load({
11825         url: "bar.php",
11826         scripts:true,
11827         params: "for=bar",
11828         text: "Loading Foo..."
11829    });
11830  * </code></pre>
11831  * @constructor
11832  * Create new UpdateManager directly.
11833  * @param {String/HTMLElement/Roo.Element} el The element to update
11834  * @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).
11835  */
11836 Roo.UpdateManager = function(el, forceNew){
11837     el = Roo.get(el);
11838     if(!forceNew && el.updateManager){
11839         return el.updateManager;
11840     }
11841     /**
11842      * The Element object
11843      * @type Roo.Element
11844      */
11845     this.el = el;
11846     /**
11847      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11848      * @type String
11849      */
11850     this.defaultUrl = null;
11851
11852     this.addEvents({
11853         /**
11854          * @event beforeupdate
11855          * Fired before an update is made, return false from your handler and the update is cancelled.
11856          * @param {Roo.Element} el
11857          * @param {String/Object/Function} url
11858          * @param {String/Object} params
11859          */
11860         "beforeupdate": true,
11861         /**
11862          * @event update
11863          * Fired after successful update is made.
11864          * @param {Roo.Element} el
11865          * @param {Object} oResponseObject The response Object
11866          */
11867         "update": true,
11868         /**
11869          * @event failure
11870          * Fired on update failure.
11871          * @param {Roo.Element} el
11872          * @param {Object} oResponseObject The response Object
11873          */
11874         "failure": true
11875     });
11876     var d = Roo.UpdateManager.defaults;
11877     /**
11878      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11879      * @type String
11880      */
11881     this.sslBlankUrl = d.sslBlankUrl;
11882     /**
11883      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11884      * @type Boolean
11885      */
11886     this.disableCaching = d.disableCaching;
11887     /**
11888      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11889      * @type String
11890      */
11891     this.indicatorText = d.indicatorText;
11892     /**
11893      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11894      * @type String
11895      */
11896     this.showLoadIndicator = d.showLoadIndicator;
11897     /**
11898      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11899      * @type Number
11900      */
11901     this.timeout = d.timeout;
11902
11903     /**
11904      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11905      * @type Boolean
11906      */
11907     this.loadScripts = d.loadScripts;
11908
11909     /**
11910      * Transaction object of current executing transaction
11911      */
11912     this.transaction = null;
11913
11914     /**
11915      * @private
11916      */
11917     this.autoRefreshProcId = null;
11918     /**
11919      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11920      * @type Function
11921      */
11922     this.refreshDelegate = this.refresh.createDelegate(this);
11923     /**
11924      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11925      * @type Function
11926      */
11927     this.updateDelegate = this.update.createDelegate(this);
11928     /**
11929      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11930      * @type Function
11931      */
11932     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11933     /**
11934      * @private
11935      */
11936     this.successDelegate = this.processSuccess.createDelegate(this);
11937     /**
11938      * @private
11939      */
11940     this.failureDelegate = this.processFailure.createDelegate(this);
11941
11942     if(!this.renderer){
11943      /**
11944       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11945       */
11946     this.renderer = new Roo.UpdateManager.BasicRenderer();
11947     }
11948     
11949     Roo.UpdateManager.superclass.constructor.call(this);
11950 };
11951
11952 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11953     /**
11954      * Get the Element this UpdateManager is bound to
11955      * @return {Roo.Element} The element
11956      */
11957     getEl : function(){
11958         return this.el;
11959     },
11960     /**
11961      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11962      * @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:
11963 <pre><code>
11964 um.update({<br/>
11965     url: "your-url.php",<br/>
11966     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11967     callback: yourFunction,<br/>
11968     scope: yourObject, //(optional scope)  <br/>
11969     discardUrl: false, <br/>
11970     nocache: false,<br/>
11971     text: "Loading...",<br/>
11972     timeout: 30,<br/>
11973     scripts: false<br/>
11974 });
11975 </code></pre>
11976      * The only required property is url. The optional properties nocache, text and scripts
11977      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11978      * @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}
11979      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11980      * @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.
11981      */
11982     update : function(url, params, callback, discardUrl){
11983         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11984             var method = this.method,
11985                 cfg;
11986             if(typeof url == "object"){ // must be config object
11987                 cfg = url;
11988                 url = cfg.url;
11989                 params = params || cfg.params;
11990                 callback = callback || cfg.callback;
11991                 discardUrl = discardUrl || cfg.discardUrl;
11992                 if(callback && cfg.scope){
11993                     callback = callback.createDelegate(cfg.scope);
11994                 }
11995                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11996                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11997                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11998                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11999                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12000             }
12001             this.showLoading();
12002             if(!discardUrl){
12003                 this.defaultUrl = url;
12004             }
12005             if(typeof url == "function"){
12006                 url = url.call(this);
12007             }
12008
12009             method = method || (params ? "POST" : "GET");
12010             if(method == "GET"){
12011                 url = this.prepareUrl(url);
12012             }
12013
12014             var o = Roo.apply(cfg ||{}, {
12015                 url : url,
12016                 params: params,
12017                 success: this.successDelegate,
12018                 failure: this.failureDelegate,
12019                 callback: undefined,
12020                 timeout: (this.timeout*1000),
12021                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12022             });
12023             Roo.log("updated manager called with timeout of " + o.timeout);
12024             this.transaction = Roo.Ajax.request(o);
12025         }
12026     },
12027
12028     /**
12029      * 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.
12030      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12031      * @param {String/HTMLElement} form The form Id or form element
12032      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12033      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12034      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12035      */
12036     formUpdate : function(form, url, reset, callback){
12037         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12038             if(typeof url == "function"){
12039                 url = url.call(this);
12040             }
12041             form = Roo.getDom(form);
12042             this.transaction = Roo.Ajax.request({
12043                 form: form,
12044                 url:url,
12045                 success: this.successDelegate,
12046                 failure: this.failureDelegate,
12047                 timeout: (this.timeout*1000),
12048                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12049             });
12050             this.showLoading.defer(1, this);
12051         }
12052     },
12053
12054     /**
12055      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12056      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12057      */
12058     refresh : function(callback){
12059         if(this.defaultUrl == null){
12060             return;
12061         }
12062         this.update(this.defaultUrl, null, callback, true);
12063     },
12064
12065     /**
12066      * Set this element to auto refresh.
12067      * @param {Number} interval How often to update (in seconds).
12068      * @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)
12069      * @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}
12070      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12071      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12072      */
12073     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12074         if(refreshNow){
12075             this.update(url || this.defaultUrl, params, callback, true);
12076         }
12077         if(this.autoRefreshProcId){
12078             clearInterval(this.autoRefreshProcId);
12079         }
12080         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12081     },
12082
12083     /**
12084      * Stop auto refresh on this element.
12085      */
12086      stopAutoRefresh : function(){
12087         if(this.autoRefreshProcId){
12088             clearInterval(this.autoRefreshProcId);
12089             delete this.autoRefreshProcId;
12090         }
12091     },
12092
12093     isAutoRefreshing : function(){
12094        return this.autoRefreshProcId ? true : false;
12095     },
12096     /**
12097      * Called to update the element to "Loading" state. Override to perform custom action.
12098      */
12099     showLoading : function(){
12100         if(this.showLoadIndicator){
12101             this.el.update(this.indicatorText);
12102         }
12103     },
12104
12105     /**
12106      * Adds unique parameter to query string if disableCaching = true
12107      * @private
12108      */
12109     prepareUrl : function(url){
12110         if(this.disableCaching){
12111             var append = "_dc=" + (new Date().getTime());
12112             if(url.indexOf("?") !== -1){
12113                 url += "&" + append;
12114             }else{
12115                 url += "?" + append;
12116             }
12117         }
12118         return url;
12119     },
12120
12121     /**
12122      * @private
12123      */
12124     processSuccess : function(response){
12125         this.transaction = null;
12126         if(response.argument.form && response.argument.reset){
12127             try{ // put in try/catch since some older FF releases had problems with this
12128                 response.argument.form.reset();
12129             }catch(e){}
12130         }
12131         if(this.loadScripts){
12132             this.renderer.render(this.el, response, this,
12133                 this.updateComplete.createDelegate(this, [response]));
12134         }else{
12135             this.renderer.render(this.el, response, this);
12136             this.updateComplete(response);
12137         }
12138     },
12139
12140     updateComplete : function(response){
12141         this.fireEvent("update", this.el, response);
12142         if(typeof response.argument.callback == "function"){
12143             response.argument.callback(this.el, true, response);
12144         }
12145     },
12146
12147     /**
12148      * @private
12149      */
12150     processFailure : function(response){
12151         this.transaction = null;
12152         this.fireEvent("failure", this.el, response);
12153         if(typeof response.argument.callback == "function"){
12154             response.argument.callback(this.el, false, response);
12155         }
12156     },
12157
12158     /**
12159      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12160      * @param {Object} renderer The object implementing the render() method
12161      */
12162     setRenderer : function(renderer){
12163         this.renderer = renderer;
12164     },
12165
12166     getRenderer : function(){
12167        return this.renderer;
12168     },
12169
12170     /**
12171      * Set the defaultUrl used for updates
12172      * @param {String/Function} defaultUrl The url or a function to call to get the url
12173      */
12174     setDefaultUrl : function(defaultUrl){
12175         this.defaultUrl = defaultUrl;
12176     },
12177
12178     /**
12179      * Aborts the executing transaction
12180      */
12181     abort : function(){
12182         if(this.transaction){
12183             Roo.Ajax.abort(this.transaction);
12184         }
12185     },
12186
12187     /**
12188      * Returns true if an update is in progress
12189      * @return {Boolean}
12190      */
12191     isUpdating : function(){
12192         if(this.transaction){
12193             return Roo.Ajax.isLoading(this.transaction);
12194         }
12195         return false;
12196     }
12197 });
12198
12199 /**
12200  * @class Roo.UpdateManager.defaults
12201  * @static (not really - but it helps the doc tool)
12202  * The defaults collection enables customizing the default properties of UpdateManager
12203  */
12204    Roo.UpdateManager.defaults = {
12205        /**
12206          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12207          * @type Number
12208          */
12209          timeout : 30,
12210
12211          /**
12212          * True to process scripts by default (Defaults to false).
12213          * @type Boolean
12214          */
12215         loadScripts : false,
12216
12217         /**
12218         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12219         * @type String
12220         */
12221         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12222         /**
12223          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12224          * @type Boolean
12225          */
12226         disableCaching : false,
12227         /**
12228          * Whether to show indicatorText when loading (Defaults to true).
12229          * @type Boolean
12230          */
12231         showLoadIndicator : true,
12232         /**
12233          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12234          * @type String
12235          */
12236         indicatorText : '<div class="loading-indicator">Loading...</div>'
12237    };
12238
12239 /**
12240  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12241  *Usage:
12242  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12243  * @param {String/HTMLElement/Roo.Element} el The element to update
12244  * @param {String} url The url
12245  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12246  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12247  * @static
12248  * @deprecated
12249  * @member Roo.UpdateManager
12250  */
12251 Roo.UpdateManager.updateElement = function(el, url, params, options){
12252     var um = Roo.get(el, true).getUpdateManager();
12253     Roo.apply(um, options);
12254     um.update(url, params, options ? options.callback : null);
12255 };
12256 // alias for backwards compat
12257 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12258 /**
12259  * @class Roo.UpdateManager.BasicRenderer
12260  * Default Content renderer. Updates the elements innerHTML with the responseText.
12261  */
12262 Roo.UpdateManager.BasicRenderer = function(){};
12263
12264 Roo.UpdateManager.BasicRenderer.prototype = {
12265     /**
12266      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12267      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12268      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12269      * @param {Roo.Element} el The element being rendered
12270      * @param {Object} response The YUI Connect response object
12271      * @param {UpdateManager} updateManager The calling update manager
12272      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12273      */
12274      render : function(el, response, updateManager, callback){
12275         el.update(response.responseText, updateManager.loadScripts, callback);
12276     }
12277 };
12278 /*
12279  * Based on:
12280  * Roo JS
12281  * (c)) Alan Knowles
12282  * Licence : LGPL
12283  */
12284
12285
12286 /**
12287  * @class Roo.DomTemplate
12288  * @extends Roo.Template
12289  * An effort at a dom based template engine..
12290  *
12291  * Similar to XTemplate, except it uses dom parsing to create the template..
12292  *
12293  * Supported features:
12294  *
12295  *  Tags:
12296
12297 <pre><code>
12298       {a_variable} - output encoded.
12299       {a_variable.format:("Y-m-d")} - call a method on the variable
12300       {a_variable:raw} - unencoded output
12301       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12302       {a_variable:this.method_on_template(...)} - call a method on the template object.
12303  
12304 </code></pre>
12305  *  The tpl tag:
12306 <pre><code>
12307         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12308         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12309         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12310         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12311   
12312 </code></pre>
12313  *      
12314  */
12315 Roo.DomTemplate = function()
12316 {
12317      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12318      if (this.html) {
12319         this.compile();
12320      }
12321 };
12322
12323
12324 Roo.extend(Roo.DomTemplate, Roo.Template, {
12325     /**
12326      * id counter for sub templates.
12327      */
12328     id : 0,
12329     /**
12330      * flag to indicate if dom parser is inside a pre,
12331      * it will strip whitespace if not.
12332      */
12333     inPre : false,
12334     
12335     /**
12336      * The various sub templates
12337      */
12338     tpls : false,
12339     
12340     
12341     
12342     /**
12343      *
12344      * basic tag replacing syntax
12345      * WORD:WORD()
12346      *
12347      * // you can fake an object call by doing this
12348      *  x.t:(test,tesT) 
12349      * 
12350      */
12351     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12352     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12353     
12354     iterChild : function (node, method) {
12355         
12356         var oldPre = this.inPre;
12357         if (node.tagName == 'PRE') {
12358             this.inPre = true;
12359         }
12360         for( var i = 0; i < node.childNodes.length; i++) {
12361             method.call(this, node.childNodes[i]);
12362         }
12363         this.inPre = oldPre;
12364     },
12365     
12366     
12367     
12368     /**
12369      * compile the template
12370      *
12371      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12372      *
12373      */
12374     compile: function()
12375     {
12376         var s = this.html;
12377         
12378         // covert the html into DOM...
12379         var doc = false;
12380         var div =false;
12381         try {
12382             doc = document.implementation.createHTMLDocument("");
12383             doc.documentElement.innerHTML =   this.html  ;
12384             div = doc.documentElement;
12385         } catch (e) {
12386             // old IE... - nasty -- it causes all sorts of issues.. with
12387             // images getting pulled from server..
12388             div = document.createElement('div');
12389             div.innerHTML = this.html;
12390         }
12391         //doc.documentElement.innerHTML = htmlBody
12392          
12393         
12394         
12395         this.tpls = [];
12396         var _t = this;
12397         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12398         
12399         var tpls = this.tpls;
12400         
12401         // create a top level template from the snippet..
12402         
12403         //Roo.log(div.innerHTML);
12404         
12405         var tpl = {
12406             uid : 'master',
12407             id : this.id++,
12408             attr : false,
12409             value : false,
12410             body : div.innerHTML,
12411             
12412             forCall : false,
12413             execCall : false,
12414             dom : div,
12415             isTop : true
12416             
12417         };
12418         tpls.unshift(tpl);
12419         
12420         
12421         // compile them...
12422         this.tpls = [];
12423         Roo.each(tpls, function(tp){
12424             this.compileTpl(tp);
12425             this.tpls[tp.id] = tp;
12426         }, this);
12427         
12428         this.master = tpls[0];
12429         return this;
12430         
12431         
12432     },
12433     
12434     compileNode : function(node, istop) {
12435         // test for
12436         //Roo.log(node);
12437         
12438         
12439         // skip anything not a tag..
12440         if (node.nodeType != 1) {
12441             if (node.nodeType == 3 && !this.inPre) {
12442                 // reduce white space..
12443                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12444                 
12445             }
12446             return;
12447         }
12448         
12449         var tpl = {
12450             uid : false,
12451             id : false,
12452             attr : false,
12453             value : false,
12454             body : '',
12455             
12456             forCall : false,
12457             execCall : false,
12458             dom : false,
12459             isTop : istop
12460             
12461             
12462         };
12463         
12464         
12465         switch(true) {
12466             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12467             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12468             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12469             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12470             // no default..
12471         }
12472         
12473         
12474         if (!tpl.attr) {
12475             // just itterate children..
12476             this.iterChild(node,this.compileNode);
12477             return;
12478         }
12479         tpl.uid = this.id++;
12480         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12481         node.removeAttribute('roo-'+ tpl.attr);
12482         if (tpl.attr != 'name') {
12483             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12484             node.parentNode.replaceChild(placeholder,  node);
12485         } else {
12486             
12487             var placeholder =  document.createElement('span');
12488             placeholder.className = 'roo-tpl-' + tpl.value;
12489             node.parentNode.replaceChild(placeholder,  node);
12490         }
12491         
12492         // parent now sees '{domtplXXXX}
12493         this.iterChild(node,this.compileNode);
12494         
12495         // we should now have node body...
12496         var div = document.createElement('div');
12497         div.appendChild(node);
12498         tpl.dom = node;
12499         // this has the unfortunate side effect of converting tagged attributes
12500         // eg. href="{...}" into %7C...%7D
12501         // this has been fixed by searching for those combo's although it's a bit hacky..
12502         
12503         
12504         tpl.body = div.innerHTML;
12505         
12506         
12507          
12508         tpl.id = tpl.uid;
12509         switch(tpl.attr) {
12510             case 'for' :
12511                 switch (tpl.value) {
12512                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12513                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12514                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12515                 }
12516                 break;
12517             
12518             case 'exec':
12519                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12520                 break;
12521             
12522             case 'if':     
12523                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12524                 break;
12525             
12526             case 'name':
12527                 tpl.id  = tpl.value; // replace non characters???
12528                 break;
12529             
12530         }
12531         
12532         
12533         this.tpls.push(tpl);
12534         
12535         
12536         
12537     },
12538     
12539     
12540     
12541     
12542     /**
12543      * Compile a segment of the template into a 'sub-template'
12544      *
12545      * 
12546      * 
12547      *
12548      */
12549     compileTpl : function(tpl)
12550     {
12551         var fm = Roo.util.Format;
12552         var useF = this.disableFormats !== true;
12553         
12554         var sep = Roo.isGecko ? "+\n" : ",\n";
12555         
12556         var undef = function(str) {
12557             Roo.debug && Roo.log("Property not found :"  + str);
12558             return '';
12559         };
12560           
12561         //Roo.log(tpl.body);
12562         
12563         
12564         
12565         var fn = function(m, lbrace, name, format, args)
12566         {
12567             //Roo.log("ARGS");
12568             //Roo.log(arguments);
12569             args = args ? args.replace(/\\'/g,"'") : args;
12570             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12571             if (typeof(format) == 'undefined') {
12572                 format =  'htmlEncode'; 
12573             }
12574             if (format == 'raw' ) {
12575                 format = false;
12576             }
12577             
12578             if(name.substr(0, 6) == 'domtpl'){
12579                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12580             }
12581             
12582             // build an array of options to determine if value is undefined..
12583             
12584             // basically get 'xxxx.yyyy' then do
12585             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12586             //    (function () { Roo.log("Property not found"); return ''; })() :
12587             //    ......
12588             
12589             var udef_ar = [];
12590             var lookfor = '';
12591             Roo.each(name.split('.'), function(st) {
12592                 lookfor += (lookfor.length ? '.': '') + st;
12593                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12594             });
12595             
12596             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12597             
12598             
12599             if(format && useF){
12600                 
12601                 args = args ? ',' + args : "";
12602                  
12603                 if(format.substr(0, 5) != "this."){
12604                     format = "fm." + format + '(';
12605                 }else{
12606                     format = 'this.call("'+ format.substr(5) + '", ';
12607                     args = ", values";
12608                 }
12609                 
12610                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12611             }
12612              
12613             if (args && args.length) {
12614                 // called with xxyx.yuu:(test,test)
12615                 // change to ()
12616                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12617             }
12618             // raw.. - :raw modifier..
12619             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12620             
12621         };
12622         var body;
12623         // branched to use + in gecko and [].join() in others
12624         if(Roo.isGecko){
12625             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12626                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12627                     "';};};";
12628         }else{
12629             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12630             body.push(tpl.body.replace(/(\r\n|\n)/g,
12631                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12632             body.push("'].join('');};};");
12633             body = body.join('');
12634         }
12635         
12636         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12637        
12638         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12639         eval(body);
12640         
12641         return this;
12642     },
12643      
12644     /**
12645      * same as applyTemplate, except it's done to one of the subTemplates
12646      * when using named templates, you can do:
12647      *
12648      * var str = pl.applySubTemplate('your-name', values);
12649      *
12650      * 
12651      * @param {Number} id of the template
12652      * @param {Object} values to apply to template
12653      * @param {Object} parent (normaly the instance of this object)
12654      */
12655     applySubTemplate : function(id, values, parent)
12656     {
12657         
12658         
12659         var t = this.tpls[id];
12660         
12661         
12662         try { 
12663             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12664                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12665                 return '';
12666             }
12667         } catch(e) {
12668             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12669             Roo.log(values);
12670           
12671             return '';
12672         }
12673         try { 
12674             
12675             if(t.execCall && t.execCall.call(this, values, parent)){
12676                 return '';
12677             }
12678         } catch(e) {
12679             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12680             Roo.log(values);
12681             return '';
12682         }
12683         
12684         try {
12685             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12686             parent = t.target ? values : parent;
12687             if(t.forCall && vs instanceof Array){
12688                 var buf = [];
12689                 for(var i = 0, len = vs.length; i < len; i++){
12690                     try {
12691                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12692                     } catch (e) {
12693                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12694                         Roo.log(e.body);
12695                         //Roo.log(t.compiled);
12696                         Roo.log(vs[i]);
12697                     }   
12698                 }
12699                 return buf.join('');
12700             }
12701         } catch (e) {
12702             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12703             Roo.log(values);
12704             return '';
12705         }
12706         try {
12707             return t.compiled.call(this, vs, parent);
12708         } catch (e) {
12709             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12710             Roo.log(e.body);
12711             //Roo.log(t.compiled);
12712             Roo.log(values);
12713             return '';
12714         }
12715     },
12716
12717    
12718
12719     applyTemplate : function(values){
12720         return this.master.compiled.call(this, values, {});
12721         //var s = this.subs;
12722     },
12723
12724     apply : function(){
12725         return this.applyTemplate.apply(this, arguments);
12726     }
12727
12728  });
12729
12730 Roo.DomTemplate.from = function(el){
12731     el = Roo.getDom(el);
12732     return new Roo.Domtemplate(el.value || el.innerHTML);
12733 };/*
12734  * Based on:
12735  * Ext JS Library 1.1.1
12736  * Copyright(c) 2006-2007, Ext JS, LLC.
12737  *
12738  * Originally Released Under LGPL - original licence link has changed is not relivant.
12739  *
12740  * Fork - LGPL
12741  * <script type="text/javascript">
12742  */
12743
12744 /**
12745  * @class Roo.util.DelayedTask
12746  * Provides a convenient method of performing setTimeout where a new
12747  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12748  * You can use this class to buffer
12749  * the keypress events for a certain number of milliseconds, and perform only if they stop
12750  * for that amount of time.
12751  * @constructor The parameters to this constructor serve as defaults and are not required.
12752  * @param {Function} fn (optional) The default function to timeout
12753  * @param {Object} scope (optional) The default scope of that timeout
12754  * @param {Array} args (optional) The default Array of arguments
12755  */
12756 Roo.util.DelayedTask = function(fn, scope, args){
12757     var id = null, d, t;
12758
12759     var call = function(){
12760         var now = new Date().getTime();
12761         if(now - t >= d){
12762             clearInterval(id);
12763             id = null;
12764             fn.apply(scope, args || []);
12765         }
12766     };
12767     /**
12768      * Cancels any pending timeout and queues a new one
12769      * @param {Number} delay The milliseconds to delay
12770      * @param {Function} newFn (optional) Overrides function passed to constructor
12771      * @param {Object} newScope (optional) Overrides scope passed to constructor
12772      * @param {Array} newArgs (optional) Overrides args passed to constructor
12773      */
12774     this.delay = function(delay, newFn, newScope, newArgs){
12775         if(id && delay != d){
12776             this.cancel();
12777         }
12778         d = delay;
12779         t = new Date().getTime();
12780         fn = newFn || fn;
12781         scope = newScope || scope;
12782         args = newArgs || args;
12783         if(!id){
12784             id = setInterval(call, d);
12785         }
12786     };
12787
12788     /**
12789      * Cancel the last queued timeout
12790      */
12791     this.cancel = function(){
12792         if(id){
12793             clearInterval(id);
12794             id = null;
12795         }
12796     };
12797 };/*
12798  * Based on:
12799  * Ext JS Library 1.1.1
12800  * Copyright(c) 2006-2007, Ext JS, LLC.
12801  *
12802  * Originally Released Under LGPL - original licence link has changed is not relivant.
12803  *
12804  * Fork - LGPL
12805  * <script type="text/javascript">
12806  */
12807  
12808  
12809 Roo.util.TaskRunner = function(interval){
12810     interval = interval || 10;
12811     var tasks = [], removeQueue = [];
12812     var id = 0;
12813     var running = false;
12814
12815     var stopThread = function(){
12816         running = false;
12817         clearInterval(id);
12818         id = 0;
12819     };
12820
12821     var startThread = function(){
12822         if(!running){
12823             running = true;
12824             id = setInterval(runTasks, interval);
12825         }
12826     };
12827
12828     var removeTask = function(task){
12829         removeQueue.push(task);
12830         if(task.onStop){
12831             task.onStop();
12832         }
12833     };
12834
12835     var runTasks = function(){
12836         if(removeQueue.length > 0){
12837             for(var i = 0, len = removeQueue.length; i < len; i++){
12838                 tasks.remove(removeQueue[i]);
12839             }
12840             removeQueue = [];
12841             if(tasks.length < 1){
12842                 stopThread();
12843                 return;
12844             }
12845         }
12846         var now = new Date().getTime();
12847         for(var i = 0, len = tasks.length; i < len; ++i){
12848             var t = tasks[i];
12849             var itime = now - t.taskRunTime;
12850             if(t.interval <= itime){
12851                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12852                 t.taskRunTime = now;
12853                 if(rt === false || t.taskRunCount === t.repeat){
12854                     removeTask(t);
12855                     return;
12856                 }
12857             }
12858             if(t.duration && t.duration <= (now - t.taskStartTime)){
12859                 removeTask(t);
12860             }
12861         }
12862     };
12863
12864     /**
12865      * Queues a new task.
12866      * @param {Object} task
12867      */
12868     this.start = function(task){
12869         tasks.push(task);
12870         task.taskStartTime = new Date().getTime();
12871         task.taskRunTime = 0;
12872         task.taskRunCount = 0;
12873         startThread();
12874         return task;
12875     };
12876
12877     this.stop = function(task){
12878         removeTask(task);
12879         return task;
12880     };
12881
12882     this.stopAll = function(){
12883         stopThread();
12884         for(var i = 0, len = tasks.length; i < len; i++){
12885             if(tasks[i].onStop){
12886                 tasks[i].onStop();
12887             }
12888         }
12889         tasks = [];
12890         removeQueue = [];
12891     };
12892 };
12893
12894 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12895  * Based on:
12896  * Ext JS Library 1.1.1
12897  * Copyright(c) 2006-2007, Ext JS, LLC.
12898  *
12899  * Originally Released Under LGPL - original licence link has changed is not relivant.
12900  *
12901  * Fork - LGPL
12902  * <script type="text/javascript">
12903  */
12904
12905  
12906 /**
12907  * @class Roo.util.MixedCollection
12908  * @extends Roo.util.Observable
12909  * A Collection class that maintains both numeric indexes and keys and exposes events.
12910  * @constructor
12911  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12912  * collection (defaults to false)
12913  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12914  * and return the key value for that item.  This is used when available to look up the key on items that
12915  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12916  * equivalent to providing an implementation for the {@link #getKey} method.
12917  */
12918 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12919     this.items = [];
12920     this.map = {};
12921     this.keys = [];
12922     this.length = 0;
12923     this.addEvents({
12924         /**
12925          * @event clear
12926          * Fires when the collection is cleared.
12927          */
12928         "clear" : true,
12929         /**
12930          * @event add
12931          * Fires when an item is added to the collection.
12932          * @param {Number} index The index at which the item was added.
12933          * @param {Object} o The item added.
12934          * @param {String} key The key associated with the added item.
12935          */
12936         "add" : true,
12937         /**
12938          * @event replace
12939          * Fires when an item is replaced in the collection.
12940          * @param {String} key he key associated with the new added.
12941          * @param {Object} old The item being replaced.
12942          * @param {Object} new The new item.
12943          */
12944         "replace" : true,
12945         /**
12946          * @event remove
12947          * Fires when an item is removed from the collection.
12948          * @param {Object} o The item being removed.
12949          * @param {String} key (optional) The key associated with the removed item.
12950          */
12951         "remove" : true,
12952         "sort" : true
12953     });
12954     this.allowFunctions = allowFunctions === true;
12955     if(keyFn){
12956         this.getKey = keyFn;
12957     }
12958     Roo.util.MixedCollection.superclass.constructor.call(this);
12959 };
12960
12961 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12962     allowFunctions : false,
12963     
12964 /**
12965  * Adds an item to the collection.
12966  * @param {String} key The key to associate with the item
12967  * @param {Object} o The item to add.
12968  * @return {Object} The item added.
12969  */
12970     add : function(key, o){
12971         if(arguments.length == 1){
12972             o = arguments[0];
12973             key = this.getKey(o);
12974         }
12975         if(typeof key == "undefined" || key === null){
12976             this.length++;
12977             this.items.push(o);
12978             this.keys.push(null);
12979         }else{
12980             var old = this.map[key];
12981             if(old){
12982                 return this.replace(key, o);
12983             }
12984             this.length++;
12985             this.items.push(o);
12986             this.map[key] = o;
12987             this.keys.push(key);
12988         }
12989         this.fireEvent("add", this.length-1, o, key);
12990         return o;
12991     },
12992        
12993 /**
12994   * MixedCollection has a generic way to fetch keys if you implement getKey.
12995 <pre><code>
12996 // normal way
12997 var mc = new Roo.util.MixedCollection();
12998 mc.add(someEl.dom.id, someEl);
12999 mc.add(otherEl.dom.id, otherEl);
13000 //and so on
13001
13002 // using getKey
13003 var mc = new Roo.util.MixedCollection();
13004 mc.getKey = function(el){
13005    return el.dom.id;
13006 };
13007 mc.add(someEl);
13008 mc.add(otherEl);
13009
13010 // or via the constructor
13011 var mc = new Roo.util.MixedCollection(false, function(el){
13012    return el.dom.id;
13013 });
13014 mc.add(someEl);
13015 mc.add(otherEl);
13016 </code></pre>
13017  * @param o {Object} The item for which to find the key.
13018  * @return {Object} The key for the passed item.
13019  */
13020     getKey : function(o){
13021          return o.id; 
13022     },
13023    
13024 /**
13025  * Replaces an item in the collection.
13026  * @param {String} key The key associated with the item to replace, or the item to replace.
13027  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13028  * @return {Object}  The new item.
13029  */
13030     replace : function(key, o){
13031         if(arguments.length == 1){
13032             o = arguments[0];
13033             key = this.getKey(o);
13034         }
13035         var old = this.item(key);
13036         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13037              return this.add(key, o);
13038         }
13039         var index = this.indexOfKey(key);
13040         this.items[index] = o;
13041         this.map[key] = o;
13042         this.fireEvent("replace", key, old, o);
13043         return o;
13044     },
13045    
13046 /**
13047  * Adds all elements of an Array or an Object to the collection.
13048  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13049  * an Array of values, each of which are added to the collection.
13050  */
13051     addAll : function(objs){
13052         if(arguments.length > 1 || objs instanceof Array){
13053             var args = arguments.length > 1 ? arguments : objs;
13054             for(var i = 0, len = args.length; i < len; i++){
13055                 this.add(args[i]);
13056             }
13057         }else{
13058             for(var key in objs){
13059                 if(this.allowFunctions || typeof objs[key] != "function"){
13060                     this.add(key, objs[key]);
13061                 }
13062             }
13063         }
13064     },
13065    
13066 /**
13067  * Executes the specified function once for every item in the collection, passing each
13068  * item as the first and only parameter. returning false from the function will stop the iteration.
13069  * @param {Function} fn The function to execute for each item.
13070  * @param {Object} scope (optional) The scope in which to execute the function.
13071  */
13072     each : function(fn, scope){
13073         var items = [].concat(this.items); // each safe for removal
13074         for(var i = 0, len = items.length; i < len; i++){
13075             if(fn.call(scope || items[i], items[i], i, len) === false){
13076                 break;
13077             }
13078         }
13079     },
13080    
13081 /**
13082  * Executes the specified function once for every key in the collection, passing each
13083  * key, and its associated item as the first two parameters.
13084  * @param {Function} fn The function to execute for each item.
13085  * @param {Object} scope (optional) The scope in which to execute the function.
13086  */
13087     eachKey : function(fn, scope){
13088         for(var i = 0, len = this.keys.length; i < len; i++){
13089             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13090         }
13091     },
13092    
13093 /**
13094  * Returns the first item in the collection which elicits a true return value from the
13095  * passed selection function.
13096  * @param {Function} fn The selection function to execute for each item.
13097  * @param {Object} scope (optional) The scope in which to execute the function.
13098  * @return {Object} The first item in the collection which returned true from the selection function.
13099  */
13100     find : function(fn, scope){
13101         for(var i = 0, len = this.items.length; i < len; i++){
13102             if(fn.call(scope || window, this.items[i], this.keys[i])){
13103                 return this.items[i];
13104             }
13105         }
13106         return null;
13107     },
13108    
13109 /**
13110  * Inserts an item at the specified index in the collection.
13111  * @param {Number} index The index to insert the item at.
13112  * @param {String} key The key to associate with the new item, or the item itself.
13113  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13114  * @return {Object} The item inserted.
13115  */
13116     insert : function(index, key, o){
13117         if(arguments.length == 2){
13118             o = arguments[1];
13119             key = this.getKey(o);
13120         }
13121         if(index >= this.length){
13122             return this.add(key, o);
13123         }
13124         this.length++;
13125         this.items.splice(index, 0, o);
13126         if(typeof key != "undefined" && key != null){
13127             this.map[key] = o;
13128         }
13129         this.keys.splice(index, 0, key);
13130         this.fireEvent("add", index, o, key);
13131         return o;
13132     },
13133    
13134 /**
13135  * Removed an item from the collection.
13136  * @param {Object} o The item to remove.
13137  * @return {Object} The item removed.
13138  */
13139     remove : function(o){
13140         return this.removeAt(this.indexOf(o));
13141     },
13142    
13143 /**
13144  * Remove an item from a specified index in the collection.
13145  * @param {Number} index The index within the collection of the item to remove.
13146  */
13147     removeAt : function(index){
13148         if(index < this.length && index >= 0){
13149             this.length--;
13150             var o = this.items[index];
13151             this.items.splice(index, 1);
13152             var key = this.keys[index];
13153             if(typeof key != "undefined"){
13154                 delete this.map[key];
13155             }
13156             this.keys.splice(index, 1);
13157             this.fireEvent("remove", o, key);
13158         }
13159     },
13160    
13161 /**
13162  * Removed an item associated with the passed key fom the collection.
13163  * @param {String} key The key of the item to remove.
13164  */
13165     removeKey : function(key){
13166         return this.removeAt(this.indexOfKey(key));
13167     },
13168    
13169 /**
13170  * Returns the number of items in the collection.
13171  * @return {Number} the number of items in the collection.
13172  */
13173     getCount : function(){
13174         return this.length; 
13175     },
13176    
13177 /**
13178  * Returns index within the collection of the passed Object.
13179  * @param {Object} o The item to find the index of.
13180  * @return {Number} index of the item.
13181  */
13182     indexOf : function(o){
13183         if(!this.items.indexOf){
13184             for(var i = 0, len = this.items.length; i < len; i++){
13185                 if(this.items[i] == o) {
13186                     return i;
13187                 }
13188             }
13189             return -1;
13190         }else{
13191             return this.items.indexOf(o);
13192         }
13193     },
13194    
13195 /**
13196  * Returns index within the collection of the passed key.
13197  * @param {String} key The key to find the index of.
13198  * @return {Number} index of the key.
13199  */
13200     indexOfKey : function(key){
13201         if(!this.keys.indexOf){
13202             for(var i = 0, len = this.keys.length; i < len; i++){
13203                 if(this.keys[i] == key) {
13204                     return i;
13205                 }
13206             }
13207             return -1;
13208         }else{
13209             return this.keys.indexOf(key);
13210         }
13211     },
13212    
13213 /**
13214  * Returns the item associated with the passed key OR index. Key has priority over index.
13215  * @param {String/Number} key The key or index of the item.
13216  * @return {Object} The item associated with the passed key.
13217  */
13218     item : function(key){
13219         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13220         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13221     },
13222     
13223 /**
13224  * Returns the item at the specified index.
13225  * @param {Number} index The index of the item.
13226  * @return {Object}
13227  */
13228     itemAt : function(index){
13229         return this.items[index];
13230     },
13231     
13232 /**
13233  * Returns the item associated with the passed key.
13234  * @param {String/Number} key The key of the item.
13235  * @return {Object} The item associated with the passed key.
13236  */
13237     key : function(key){
13238         return this.map[key];
13239     },
13240    
13241 /**
13242  * Returns true if the collection contains the passed Object as an item.
13243  * @param {Object} o  The Object to look for in the collection.
13244  * @return {Boolean} True if the collection contains the Object as an item.
13245  */
13246     contains : function(o){
13247         return this.indexOf(o) != -1;
13248     },
13249    
13250 /**
13251  * Returns true if the collection contains the passed Object as a key.
13252  * @param {String} key The key to look for in the collection.
13253  * @return {Boolean} True if the collection contains the Object as a key.
13254  */
13255     containsKey : function(key){
13256         return typeof this.map[key] != "undefined";
13257     },
13258    
13259 /**
13260  * Removes all items from the collection.
13261  */
13262     clear : function(){
13263         this.length = 0;
13264         this.items = [];
13265         this.keys = [];
13266         this.map = {};
13267         this.fireEvent("clear");
13268     },
13269    
13270 /**
13271  * Returns the first item in the collection.
13272  * @return {Object} the first item in the collection..
13273  */
13274     first : function(){
13275         return this.items[0]; 
13276     },
13277    
13278 /**
13279  * Returns the last item in the collection.
13280  * @return {Object} the last item in the collection..
13281  */
13282     last : function(){
13283         return this.items[this.length-1];   
13284     },
13285     
13286     _sort : function(property, dir, fn){
13287         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13288         fn = fn || function(a, b){
13289             return a-b;
13290         };
13291         var c = [], k = this.keys, items = this.items;
13292         for(var i = 0, len = items.length; i < len; i++){
13293             c[c.length] = {key: k[i], value: items[i], index: i};
13294         }
13295         c.sort(function(a, b){
13296             var v = fn(a[property], b[property]) * dsc;
13297             if(v == 0){
13298                 v = (a.index < b.index ? -1 : 1);
13299             }
13300             return v;
13301         });
13302         for(var i = 0, len = c.length; i < len; i++){
13303             items[i] = c[i].value;
13304             k[i] = c[i].key;
13305         }
13306         this.fireEvent("sort", this);
13307     },
13308     
13309     /**
13310      * Sorts this collection with the passed comparison function
13311      * @param {String} direction (optional) "ASC" or "DESC"
13312      * @param {Function} fn (optional) comparison function
13313      */
13314     sort : function(dir, fn){
13315         this._sort("value", dir, fn);
13316     },
13317     
13318     /**
13319      * Sorts this collection by keys
13320      * @param {String} direction (optional) "ASC" or "DESC"
13321      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13322      */
13323     keySort : function(dir, fn){
13324         this._sort("key", dir, fn || function(a, b){
13325             return String(a).toUpperCase()-String(b).toUpperCase();
13326         });
13327     },
13328     
13329     /**
13330      * Returns a range of items in this collection
13331      * @param {Number} startIndex (optional) defaults to 0
13332      * @param {Number} endIndex (optional) default to the last item
13333      * @return {Array} An array of items
13334      */
13335     getRange : function(start, end){
13336         var items = this.items;
13337         if(items.length < 1){
13338             return [];
13339         }
13340         start = start || 0;
13341         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13342         var r = [];
13343         if(start <= end){
13344             for(var i = start; i <= end; i++) {
13345                     r[r.length] = items[i];
13346             }
13347         }else{
13348             for(var i = start; i >= end; i--) {
13349                     r[r.length] = items[i];
13350             }
13351         }
13352         return r;
13353     },
13354         
13355     /**
13356      * Filter the <i>objects</i> in this collection by a specific property. 
13357      * Returns a new collection that has been filtered.
13358      * @param {String} property A property on your objects
13359      * @param {String/RegExp} value Either string that the property values 
13360      * should start with or a RegExp to test against the property
13361      * @return {MixedCollection} The new filtered collection
13362      */
13363     filter : function(property, value){
13364         if(!value.exec){ // not a regex
13365             value = String(value);
13366             if(value.length == 0){
13367                 return this.clone();
13368             }
13369             value = new RegExp("^" + Roo.escapeRe(value), "i");
13370         }
13371         return this.filterBy(function(o){
13372             return o && value.test(o[property]);
13373         });
13374         },
13375     
13376     /**
13377      * Filter by a function. * Returns a new collection that has been filtered.
13378      * The passed function will be called with each 
13379      * object in the collection. If the function returns true, the value is included 
13380      * otherwise it is filtered.
13381      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13382      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13383      * @return {MixedCollection} The new filtered collection
13384      */
13385     filterBy : function(fn, scope){
13386         var r = new Roo.util.MixedCollection();
13387         r.getKey = this.getKey;
13388         var k = this.keys, it = this.items;
13389         for(var i = 0, len = it.length; i < len; i++){
13390             if(fn.call(scope||this, it[i], k[i])){
13391                                 r.add(k[i], it[i]);
13392                         }
13393         }
13394         return r;
13395     },
13396     
13397     /**
13398      * Creates a duplicate of this collection
13399      * @return {MixedCollection}
13400      */
13401     clone : function(){
13402         var r = new Roo.util.MixedCollection();
13403         var k = this.keys, it = this.items;
13404         for(var i = 0, len = it.length; i < len; i++){
13405             r.add(k[i], it[i]);
13406         }
13407         r.getKey = this.getKey;
13408         return r;
13409     }
13410 });
13411 /**
13412  * Returns the item associated with the passed key or index.
13413  * @method
13414  * @param {String/Number} key The key or index of the item.
13415  * @return {Object} The item associated with the passed key.
13416  */
13417 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13418  * Based on:
13419  * Ext JS Library 1.1.1
13420  * Copyright(c) 2006-2007, Ext JS, LLC.
13421  *
13422  * Originally Released Under LGPL - original licence link has changed is not relivant.
13423  *
13424  * Fork - LGPL
13425  * <script type="text/javascript">
13426  */
13427 /**
13428  * @class Roo.util.JSON
13429  * Modified version of Douglas Crockford"s json.js that doesn"t
13430  * mess with the Object prototype 
13431  * http://www.json.org/js.html
13432  * @singleton
13433  */
13434 Roo.util.JSON = new (function(){
13435     var useHasOwn = {}.hasOwnProperty ? true : false;
13436     
13437     // crashes Safari in some instances
13438     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13439     
13440     var pad = function(n) {
13441         return n < 10 ? "0" + n : n;
13442     };
13443     
13444     var m = {
13445         "\b": '\\b',
13446         "\t": '\\t',
13447         "\n": '\\n',
13448         "\f": '\\f',
13449         "\r": '\\r',
13450         '"' : '\\"',
13451         "\\": '\\\\'
13452     };
13453
13454     var encodeString = function(s){
13455         if (/["\\\x00-\x1f]/.test(s)) {
13456             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13457                 var c = m[b];
13458                 if(c){
13459                     return c;
13460                 }
13461                 c = b.charCodeAt();
13462                 return "\\u00" +
13463                     Math.floor(c / 16).toString(16) +
13464                     (c % 16).toString(16);
13465             }) + '"';
13466         }
13467         return '"' + s + '"';
13468     };
13469     
13470     var encodeArray = function(o){
13471         var a = ["["], b, i, l = o.length, v;
13472             for (i = 0; i < l; i += 1) {
13473                 v = o[i];
13474                 switch (typeof v) {
13475                     case "undefined":
13476                     case "function":
13477                     case "unknown":
13478                         break;
13479                     default:
13480                         if (b) {
13481                             a.push(',');
13482                         }
13483                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13484                         b = true;
13485                 }
13486             }
13487             a.push("]");
13488             return a.join("");
13489     };
13490     
13491     var encodeDate = function(o){
13492         return '"' + o.getFullYear() + "-" +
13493                 pad(o.getMonth() + 1) + "-" +
13494                 pad(o.getDate()) + "T" +
13495                 pad(o.getHours()) + ":" +
13496                 pad(o.getMinutes()) + ":" +
13497                 pad(o.getSeconds()) + '"';
13498     };
13499     
13500     /**
13501      * Encodes an Object, Array or other value
13502      * @param {Mixed} o The variable to encode
13503      * @return {String} The JSON string
13504      */
13505     this.encode = function(o)
13506     {
13507         // should this be extended to fully wrap stringify..
13508         
13509         if(typeof o == "undefined" || o === null){
13510             return "null";
13511         }else if(o instanceof Array){
13512             return encodeArray(o);
13513         }else if(o instanceof Date){
13514             return encodeDate(o);
13515         }else if(typeof o == "string"){
13516             return encodeString(o);
13517         }else if(typeof o == "number"){
13518             return isFinite(o) ? String(o) : "null";
13519         }else if(typeof o == "boolean"){
13520             return String(o);
13521         }else {
13522             var a = ["{"], b, i, v;
13523             for (i in o) {
13524                 if(!useHasOwn || o.hasOwnProperty(i)) {
13525                     v = o[i];
13526                     switch (typeof v) {
13527                     case "undefined":
13528                     case "function":
13529                     case "unknown":
13530                         break;
13531                     default:
13532                         if(b){
13533                             a.push(',');
13534                         }
13535                         a.push(this.encode(i), ":",
13536                                 v === null ? "null" : this.encode(v));
13537                         b = true;
13538                     }
13539                 }
13540             }
13541             a.push("}");
13542             return a.join("");
13543         }
13544     };
13545     
13546     /**
13547      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13548      * @param {String} json The JSON string
13549      * @return {Object} The resulting object
13550      */
13551     this.decode = function(json){
13552         
13553         return  /** eval:var:json */ eval("(" + json + ')');
13554     };
13555 })();
13556 /** 
13557  * Shorthand for {@link Roo.util.JSON#encode}
13558  * @member Roo encode 
13559  * @method */
13560 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13561 /** 
13562  * Shorthand for {@link Roo.util.JSON#decode}
13563  * @member Roo decode 
13564  * @method */
13565 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13566 /*
13567  * Based on:
13568  * Ext JS Library 1.1.1
13569  * Copyright(c) 2006-2007, Ext JS, LLC.
13570  *
13571  * Originally Released Under LGPL - original licence link has changed is not relivant.
13572  *
13573  * Fork - LGPL
13574  * <script type="text/javascript">
13575  */
13576  
13577 /**
13578  * @class Roo.util.Format
13579  * Reusable data formatting functions
13580  * @singleton
13581  */
13582 Roo.util.Format = function(){
13583     var trimRe = /^\s+|\s+$/g;
13584     return {
13585         /**
13586          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13587          * @param {String} value The string to truncate
13588          * @param {Number} length The maximum length to allow before truncating
13589          * @return {String} The converted text
13590          */
13591         ellipsis : function(value, len){
13592             if(value && value.length > len){
13593                 return value.substr(0, len-3)+"...";
13594             }
13595             return value;
13596         },
13597
13598         /**
13599          * Checks a reference and converts it to empty string if it is undefined
13600          * @param {Mixed} value Reference to check
13601          * @return {Mixed} Empty string if converted, otherwise the original value
13602          */
13603         undef : function(value){
13604             return typeof value != "undefined" ? value : "";
13605         },
13606
13607         /**
13608          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13609          * @param {String} value The string to encode
13610          * @return {String} The encoded text
13611          */
13612         htmlEncode : function(value){
13613             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13614         },
13615
13616         /**
13617          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13618          * @param {String} value The string to decode
13619          * @return {String} The decoded text
13620          */
13621         htmlDecode : function(value){
13622             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13623         },
13624
13625         /**
13626          * Trims any whitespace from either side of a string
13627          * @param {String} value The text to trim
13628          * @return {String} The trimmed text
13629          */
13630         trim : function(value){
13631             return String(value).replace(trimRe, "");
13632         },
13633
13634         /**
13635          * Returns a substring from within an original string
13636          * @param {String} value The original text
13637          * @param {Number} start The start index of the substring
13638          * @param {Number} length The length of the substring
13639          * @return {String} The substring
13640          */
13641         substr : function(value, start, length){
13642             return String(value).substr(start, length);
13643         },
13644
13645         /**
13646          * Converts a string to all lower case letters
13647          * @param {String} value The text to convert
13648          * @return {String} The converted text
13649          */
13650         lowercase : function(value){
13651             return String(value).toLowerCase();
13652         },
13653
13654         /**
13655          * Converts a string to all upper case letters
13656          * @param {String} value The text to convert
13657          * @return {String} The converted text
13658          */
13659         uppercase : function(value){
13660             return String(value).toUpperCase();
13661         },
13662
13663         /**
13664          * Converts the first character only of a string to upper case
13665          * @param {String} value The text to convert
13666          * @return {String} The converted text
13667          */
13668         capitalize : function(value){
13669             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13670         },
13671
13672         // private
13673         call : function(value, fn){
13674             if(arguments.length > 2){
13675                 var args = Array.prototype.slice.call(arguments, 2);
13676                 args.unshift(value);
13677                  
13678                 return /** eval:var:value */  eval(fn).apply(window, args);
13679             }else{
13680                 /** eval:var:value */
13681                 return /** eval:var:value */ eval(fn).call(window, value);
13682             }
13683         },
13684
13685        
13686         /**
13687          * safer version of Math.toFixed..??/
13688          * @param {Number/String} value The numeric value to format
13689          * @param {Number/String} value Decimal places 
13690          * @return {String} The formatted currency string
13691          */
13692         toFixed : function(v, n)
13693         {
13694             // why not use to fixed - precision is buggered???
13695             if (!n) {
13696                 return Math.round(v-0);
13697             }
13698             var fact = Math.pow(10,n+1);
13699             v = (Math.round((v-0)*fact))/fact;
13700             var z = (''+fact).substring(2);
13701             if (v == Math.floor(v)) {
13702                 return Math.floor(v) + '.' + z;
13703             }
13704             
13705             // now just padd decimals..
13706             var ps = String(v).split('.');
13707             var fd = (ps[1] + z);
13708             var r = fd.substring(0,n); 
13709             var rm = fd.substring(n); 
13710             if (rm < 5) {
13711                 return ps[0] + '.' + r;
13712             }
13713             r*=1; // turn it into a number;
13714             r++;
13715             if (String(r).length != n) {
13716                 ps[0]*=1;
13717                 ps[0]++;
13718                 r = String(r).substring(1); // chop the end off.
13719             }
13720             
13721             return ps[0] + '.' + r;
13722              
13723         },
13724         
13725         /**
13726          * Format a number as US currency
13727          * @param {Number/String} value The numeric value to format
13728          * @return {String} The formatted currency string
13729          */
13730         usMoney : function(v){
13731             return '$' + Roo.util.Format.number(v);
13732         },
13733         
13734         /**
13735          * Format a number
13736          * eventually this should probably emulate php's number_format
13737          * @param {Number/String} value The numeric value to format
13738          * @param {Number} decimals number of decimal places
13739          * @return {String} The formatted currency string
13740          */
13741         number : function(v,decimals)
13742         {
13743             // multiply and round.
13744             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13745             var mul = Math.pow(10, decimals);
13746             var zero = String(mul).substring(1);
13747             v = (Math.round((v-0)*mul))/mul;
13748             
13749             // if it's '0' number.. then
13750             
13751             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13752             v = String(v);
13753             var ps = v.split('.');
13754             var whole = ps[0];
13755             
13756             
13757             var r = /(\d+)(\d{3})/;
13758             // add comma's
13759             while (r.test(whole)) {
13760                 whole = whole.replace(r, '$1' + ',' + '$2');
13761             }
13762             
13763             
13764             var sub = ps[1] ?
13765                     // has decimals..
13766                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13767                     // does not have decimals
13768                     (decimals ? ('.' + zero) : '');
13769             
13770             
13771             return whole + sub ;
13772         },
13773         
13774         /**
13775          * Parse a value into a formatted date using the specified format pattern.
13776          * @param {Mixed} value The value to format
13777          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13778          * @return {String} The formatted date string
13779          */
13780         date : function(v, format){
13781             if(!v){
13782                 return "";
13783             }
13784             if(!(v instanceof Date)){
13785                 v = new Date(Date.parse(v));
13786             }
13787             return v.dateFormat(format || Roo.util.Format.defaults.date);
13788         },
13789
13790         /**
13791          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13792          * @param {String} format Any valid date format string
13793          * @return {Function} The date formatting function
13794          */
13795         dateRenderer : function(format){
13796             return function(v){
13797                 return Roo.util.Format.date(v, format);  
13798             };
13799         },
13800
13801         // private
13802         stripTagsRE : /<\/?[^>]+>/gi,
13803         
13804         /**
13805          * Strips all HTML tags
13806          * @param {Mixed} value The text from which to strip tags
13807          * @return {String} The stripped text
13808          */
13809         stripTags : function(v){
13810             return !v ? v : String(v).replace(this.stripTagsRE, "");
13811         }
13812     };
13813 }();
13814 Roo.util.Format.defaults = {
13815     date : 'd/M/Y'
13816 };/*
13817  * Based on:
13818  * Ext JS Library 1.1.1
13819  * Copyright(c) 2006-2007, Ext JS, LLC.
13820  *
13821  * Originally Released Under LGPL - original licence link has changed is not relivant.
13822  *
13823  * Fork - LGPL
13824  * <script type="text/javascript">
13825  */
13826
13827
13828  
13829
13830 /**
13831  * @class Roo.MasterTemplate
13832  * @extends Roo.Template
13833  * Provides a template that can have child templates. The syntax is:
13834 <pre><code>
13835 var t = new Roo.MasterTemplate(
13836         '&lt;select name="{name}"&gt;',
13837                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13838         '&lt;/select&gt;'
13839 );
13840 t.add('options', {value: 'foo', text: 'bar'});
13841 // or you can add multiple child elements in one shot
13842 t.addAll('options', [
13843     {value: 'foo', text: 'bar'},
13844     {value: 'foo2', text: 'bar2'},
13845     {value: 'foo3', text: 'bar3'}
13846 ]);
13847 // then append, applying the master template values
13848 t.append('my-form', {name: 'my-select'});
13849 </code></pre>
13850 * A name attribute for the child template is not required if you have only one child
13851 * template or you want to refer to them by index.
13852  */
13853 Roo.MasterTemplate = function(){
13854     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13855     this.originalHtml = this.html;
13856     var st = {};
13857     var m, re = this.subTemplateRe;
13858     re.lastIndex = 0;
13859     var subIndex = 0;
13860     while(m = re.exec(this.html)){
13861         var name = m[1], content = m[2];
13862         st[subIndex] = {
13863             name: name,
13864             index: subIndex,
13865             buffer: [],
13866             tpl : new Roo.Template(content)
13867         };
13868         if(name){
13869             st[name] = st[subIndex];
13870         }
13871         st[subIndex].tpl.compile();
13872         st[subIndex].tpl.call = this.call.createDelegate(this);
13873         subIndex++;
13874     }
13875     this.subCount = subIndex;
13876     this.subs = st;
13877 };
13878 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13879     /**
13880     * The regular expression used to match sub templates
13881     * @type RegExp
13882     * @property
13883     */
13884     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13885
13886     /**
13887      * Applies the passed values to a child template.
13888      * @param {String/Number} name (optional) The name or index of the child template
13889      * @param {Array/Object} values The values to be applied to the template
13890      * @return {MasterTemplate} this
13891      */
13892      add : function(name, values){
13893         if(arguments.length == 1){
13894             values = arguments[0];
13895             name = 0;
13896         }
13897         var s = this.subs[name];
13898         s.buffer[s.buffer.length] = s.tpl.apply(values);
13899         return this;
13900     },
13901
13902     /**
13903      * Applies all the passed values to a child template.
13904      * @param {String/Number} name (optional) The name or index of the child template
13905      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13906      * @param {Boolean} reset (optional) True to reset the template first
13907      * @return {MasterTemplate} this
13908      */
13909     fill : function(name, values, reset){
13910         var a = arguments;
13911         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13912             values = a[0];
13913             name = 0;
13914             reset = a[1];
13915         }
13916         if(reset){
13917             this.reset();
13918         }
13919         for(var i = 0, len = values.length; i < len; i++){
13920             this.add(name, values[i]);
13921         }
13922         return this;
13923     },
13924
13925     /**
13926      * Resets the template for reuse
13927      * @return {MasterTemplate} this
13928      */
13929      reset : function(){
13930         var s = this.subs;
13931         for(var i = 0; i < this.subCount; i++){
13932             s[i].buffer = [];
13933         }
13934         return this;
13935     },
13936
13937     applyTemplate : function(values){
13938         var s = this.subs;
13939         var replaceIndex = -1;
13940         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13941             return s[++replaceIndex].buffer.join("");
13942         });
13943         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13944     },
13945
13946     apply : function(){
13947         return this.applyTemplate.apply(this, arguments);
13948     },
13949
13950     compile : function(){return this;}
13951 });
13952
13953 /**
13954  * Alias for fill().
13955  * @method
13956  */
13957 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13958  /**
13959  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13960  * var tpl = Roo.MasterTemplate.from('element-id');
13961  * @param {String/HTMLElement} el
13962  * @param {Object} config
13963  * @static
13964  */
13965 Roo.MasterTemplate.from = function(el, config){
13966     el = Roo.getDom(el);
13967     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13968 };/*
13969  * Based on:
13970  * Ext JS Library 1.1.1
13971  * Copyright(c) 2006-2007, Ext JS, LLC.
13972  *
13973  * Originally Released Under LGPL - original licence link has changed is not relivant.
13974  *
13975  * Fork - LGPL
13976  * <script type="text/javascript">
13977  */
13978
13979  
13980 /**
13981  * @class Roo.util.CSS
13982  * Utility class for manipulating CSS rules
13983  * @singleton
13984  */
13985 Roo.util.CSS = function(){
13986         var rules = null;
13987         var doc = document;
13988
13989     var camelRe = /(-[a-z])/gi;
13990     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13991
13992    return {
13993    /**
13994     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13995     * tag and appended to the HEAD of the document.
13996     * @param {String|Object} cssText The text containing the css rules
13997     * @param {String} id An id to add to the stylesheet for later removal
13998     * @return {StyleSheet}
13999     */
14000     createStyleSheet : function(cssText, id){
14001         var ss;
14002         var head = doc.getElementsByTagName("head")[0];
14003         var nrules = doc.createElement("style");
14004         nrules.setAttribute("type", "text/css");
14005         if(id){
14006             nrules.setAttribute("id", id);
14007         }
14008         if (typeof(cssText) != 'string') {
14009             // support object maps..
14010             // not sure if this a good idea.. 
14011             // perhaps it should be merged with the general css handling
14012             // and handle js style props.
14013             var cssTextNew = [];
14014             for(var n in cssText) {
14015                 var citems = [];
14016                 for(var k in cssText[n]) {
14017                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14018                 }
14019                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14020                 
14021             }
14022             cssText = cssTextNew.join("\n");
14023             
14024         }
14025        
14026        
14027        if(Roo.isIE){
14028            head.appendChild(nrules);
14029            ss = nrules.styleSheet;
14030            ss.cssText = cssText;
14031        }else{
14032            try{
14033                 nrules.appendChild(doc.createTextNode(cssText));
14034            }catch(e){
14035                nrules.cssText = cssText; 
14036            }
14037            head.appendChild(nrules);
14038            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14039        }
14040        this.cacheStyleSheet(ss);
14041        return ss;
14042    },
14043
14044    /**
14045     * Removes a style or link tag by id
14046     * @param {String} id The id of the tag
14047     */
14048    removeStyleSheet : function(id){
14049        var existing = doc.getElementById(id);
14050        if(existing){
14051            existing.parentNode.removeChild(existing);
14052        }
14053    },
14054
14055    /**
14056     * Dynamically swaps an existing stylesheet reference for a new one
14057     * @param {String} id The id of an existing link tag to remove
14058     * @param {String} url The href of the new stylesheet to include
14059     */
14060    swapStyleSheet : function(id, url){
14061        this.removeStyleSheet(id);
14062        var ss = doc.createElement("link");
14063        ss.setAttribute("rel", "stylesheet");
14064        ss.setAttribute("type", "text/css");
14065        ss.setAttribute("id", id);
14066        ss.setAttribute("href", url);
14067        doc.getElementsByTagName("head")[0].appendChild(ss);
14068    },
14069    
14070    /**
14071     * Refresh the rule cache if you have dynamically added stylesheets
14072     * @return {Object} An object (hash) of rules indexed by selector
14073     */
14074    refreshCache : function(){
14075        return this.getRules(true);
14076    },
14077
14078    // private
14079    cacheStyleSheet : function(stylesheet){
14080        if(!rules){
14081            rules = {};
14082        }
14083        try{// try catch for cross domain access issue
14084            var ssRules = stylesheet.cssRules || stylesheet.rules;
14085            for(var j = ssRules.length-1; j >= 0; --j){
14086                rules[ssRules[j].selectorText] = ssRules[j];
14087            }
14088        }catch(e){}
14089    },
14090    
14091    /**
14092     * Gets all css rules for the document
14093     * @param {Boolean} refreshCache true to refresh the internal cache
14094     * @return {Object} An object (hash) of rules indexed by selector
14095     */
14096    getRules : function(refreshCache){
14097                 if(rules == null || refreshCache){
14098                         rules = {};
14099                         var ds = doc.styleSheets;
14100                         for(var i =0, len = ds.length; i < len; i++){
14101                             try{
14102                         this.cacheStyleSheet(ds[i]);
14103                     }catch(e){} 
14104                 }
14105                 }
14106                 return rules;
14107         },
14108         
14109         /**
14110     * Gets an an individual CSS rule by selector(s)
14111     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14112     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14113     * @return {CSSRule} The CSS rule or null if one is not found
14114     */
14115    getRule : function(selector, refreshCache){
14116                 var rs = this.getRules(refreshCache);
14117                 if(!(selector instanceof Array)){
14118                     return rs[selector];
14119                 }
14120                 for(var i = 0; i < selector.length; i++){
14121                         if(rs[selector[i]]){
14122                                 return rs[selector[i]];
14123                         }
14124                 }
14125                 return null;
14126         },
14127         
14128         
14129         /**
14130     * Updates a rule property
14131     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14132     * @param {String} property The css property
14133     * @param {String} value The new value for the property
14134     * @return {Boolean} true If a rule was found and updated
14135     */
14136    updateRule : function(selector, property, value){
14137                 if(!(selector instanceof Array)){
14138                         var rule = this.getRule(selector);
14139                         if(rule){
14140                                 rule.style[property.replace(camelRe, camelFn)] = value;
14141                                 return true;
14142                         }
14143                 }else{
14144                         for(var i = 0; i < selector.length; i++){
14145                                 if(this.updateRule(selector[i], property, value)){
14146                                         return true;
14147                                 }
14148                         }
14149                 }
14150                 return false;
14151         }
14152    };   
14153 }();/*
14154  * Based on:
14155  * Ext JS Library 1.1.1
14156  * Copyright(c) 2006-2007, Ext JS, LLC.
14157  *
14158  * Originally Released Under LGPL - original licence link has changed is not relivant.
14159  *
14160  * Fork - LGPL
14161  * <script type="text/javascript">
14162  */
14163
14164  
14165
14166 /**
14167  * @class Roo.util.ClickRepeater
14168  * @extends Roo.util.Observable
14169  * 
14170  * A wrapper class which can be applied to any element. Fires a "click" event while the
14171  * mouse is pressed. The interval between firings may be specified in the config but
14172  * defaults to 10 milliseconds.
14173  * 
14174  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14175  * 
14176  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14177  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14178  * Similar to an autorepeat key delay.
14179  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14180  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14181  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14182  *           "interval" and "delay" are ignored. "immediate" is honored.
14183  * @cfg {Boolean} preventDefault True to prevent the default click event
14184  * @cfg {Boolean} stopDefault True to stop the default click event
14185  * 
14186  * @history
14187  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14188  *     2007-02-02 jvs Renamed to ClickRepeater
14189  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14190  *
14191  *  @constructor
14192  * @param {String/HTMLElement/Element} el The element to listen on
14193  * @param {Object} config
14194  **/
14195 Roo.util.ClickRepeater = function(el, config)
14196 {
14197     this.el = Roo.get(el);
14198     this.el.unselectable();
14199
14200     Roo.apply(this, config);
14201
14202     this.addEvents({
14203     /**
14204      * @event mousedown
14205      * Fires when the mouse button is depressed.
14206      * @param {Roo.util.ClickRepeater} this
14207      */
14208         "mousedown" : true,
14209     /**
14210      * @event click
14211      * Fires on a specified interval during the time the element is pressed.
14212      * @param {Roo.util.ClickRepeater} this
14213      */
14214         "click" : true,
14215     /**
14216      * @event mouseup
14217      * Fires when the mouse key is released.
14218      * @param {Roo.util.ClickRepeater} this
14219      */
14220         "mouseup" : true
14221     });
14222
14223     this.el.on("mousedown", this.handleMouseDown, this);
14224     if(this.preventDefault || this.stopDefault){
14225         this.el.on("click", function(e){
14226             if(this.preventDefault){
14227                 e.preventDefault();
14228             }
14229             if(this.stopDefault){
14230                 e.stopEvent();
14231             }
14232         }, this);
14233     }
14234
14235     // allow inline handler
14236     if(this.handler){
14237         this.on("click", this.handler,  this.scope || this);
14238     }
14239
14240     Roo.util.ClickRepeater.superclass.constructor.call(this);
14241 };
14242
14243 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14244     interval : 20,
14245     delay: 250,
14246     preventDefault : true,
14247     stopDefault : false,
14248     timer : 0,
14249
14250     // private
14251     handleMouseDown : function(){
14252         clearTimeout(this.timer);
14253         this.el.blur();
14254         if(this.pressClass){
14255             this.el.addClass(this.pressClass);
14256         }
14257         this.mousedownTime = new Date();
14258
14259         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14260         this.el.on("mouseout", this.handleMouseOut, this);
14261
14262         this.fireEvent("mousedown", this);
14263         this.fireEvent("click", this);
14264         
14265         this.timer = this.click.defer(this.delay || this.interval, this);
14266     },
14267
14268     // private
14269     click : function(){
14270         this.fireEvent("click", this);
14271         this.timer = this.click.defer(this.getInterval(), this);
14272     },
14273
14274     // private
14275     getInterval: function(){
14276         if(!this.accelerate){
14277             return this.interval;
14278         }
14279         var pressTime = this.mousedownTime.getElapsed();
14280         if(pressTime < 500){
14281             return 400;
14282         }else if(pressTime < 1700){
14283             return 320;
14284         }else if(pressTime < 2600){
14285             return 250;
14286         }else if(pressTime < 3500){
14287             return 180;
14288         }else if(pressTime < 4400){
14289             return 140;
14290         }else if(pressTime < 5300){
14291             return 80;
14292         }else if(pressTime < 6200){
14293             return 50;
14294         }else{
14295             return 10;
14296         }
14297     },
14298
14299     // private
14300     handleMouseOut : function(){
14301         clearTimeout(this.timer);
14302         if(this.pressClass){
14303             this.el.removeClass(this.pressClass);
14304         }
14305         this.el.on("mouseover", this.handleMouseReturn, this);
14306     },
14307
14308     // private
14309     handleMouseReturn : function(){
14310         this.el.un("mouseover", this.handleMouseReturn);
14311         if(this.pressClass){
14312             this.el.addClass(this.pressClass);
14313         }
14314         this.click();
14315     },
14316
14317     // private
14318     handleMouseUp : function(){
14319         clearTimeout(this.timer);
14320         this.el.un("mouseover", this.handleMouseReturn);
14321         this.el.un("mouseout", this.handleMouseOut);
14322         Roo.get(document).un("mouseup", this.handleMouseUp);
14323         this.el.removeClass(this.pressClass);
14324         this.fireEvent("mouseup", this);
14325     }
14326 });/*
14327  * Based on:
14328  * Ext JS Library 1.1.1
14329  * Copyright(c) 2006-2007, Ext JS, LLC.
14330  *
14331  * Originally Released Under LGPL - original licence link has changed is not relivant.
14332  *
14333  * Fork - LGPL
14334  * <script type="text/javascript">
14335  */
14336
14337  
14338 /**
14339  * @class Roo.KeyNav
14340  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14341  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14342  * way to implement custom navigation schemes for any UI component.</p>
14343  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14344  * pageUp, pageDown, del, home, end.  Usage:</p>
14345  <pre><code>
14346 var nav = new Roo.KeyNav("my-element", {
14347     "left" : function(e){
14348         this.moveLeft(e.ctrlKey);
14349     },
14350     "right" : function(e){
14351         this.moveRight(e.ctrlKey);
14352     },
14353     "enter" : function(e){
14354         this.save();
14355     },
14356     scope : this
14357 });
14358 </code></pre>
14359  * @constructor
14360  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14361  * @param {Object} config The config
14362  */
14363 Roo.KeyNav = function(el, config){
14364     this.el = Roo.get(el);
14365     Roo.apply(this, config);
14366     if(!this.disabled){
14367         this.disabled = true;
14368         this.enable();
14369     }
14370 };
14371
14372 Roo.KeyNav.prototype = {
14373     /**
14374      * @cfg {Boolean} disabled
14375      * True to disable this KeyNav instance (defaults to false)
14376      */
14377     disabled : false,
14378     /**
14379      * @cfg {String} defaultEventAction
14380      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14381      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14382      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14383      */
14384     defaultEventAction: "stopEvent",
14385     /**
14386      * @cfg {Boolean} forceKeyDown
14387      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14388      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14389      * handle keydown instead of keypress.
14390      */
14391     forceKeyDown : false,
14392
14393     // private
14394     prepareEvent : function(e){
14395         var k = e.getKey();
14396         var h = this.keyToHandler[k];
14397         //if(h && this[h]){
14398         //    e.stopPropagation();
14399         //}
14400         if(Roo.isSafari && h && k >= 37 && k <= 40){
14401             e.stopEvent();
14402         }
14403     },
14404
14405     // private
14406     relay : function(e){
14407         var k = e.getKey();
14408         var h = this.keyToHandler[k];
14409         if(h && this[h]){
14410             if(this.doRelay(e, this[h], h) !== true){
14411                 e[this.defaultEventAction]();
14412             }
14413         }
14414     },
14415
14416     // private
14417     doRelay : function(e, h, hname){
14418         return h.call(this.scope || this, e);
14419     },
14420
14421     // possible handlers
14422     enter : false,
14423     left : false,
14424     right : false,
14425     up : false,
14426     down : false,
14427     tab : false,
14428     esc : false,
14429     pageUp : false,
14430     pageDown : false,
14431     del : false,
14432     home : false,
14433     end : false,
14434
14435     // quick lookup hash
14436     keyToHandler : {
14437         37 : "left",
14438         39 : "right",
14439         38 : "up",
14440         40 : "down",
14441         33 : "pageUp",
14442         34 : "pageDown",
14443         46 : "del",
14444         36 : "home",
14445         35 : "end",
14446         13 : "enter",
14447         27 : "esc",
14448         9  : "tab"
14449     },
14450
14451         /**
14452          * Enable this KeyNav
14453          */
14454         enable: function(){
14455                 if(this.disabled){
14456             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14457             // the EventObject will normalize Safari automatically
14458             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14459                 this.el.on("keydown", this.relay,  this);
14460             }else{
14461                 this.el.on("keydown", this.prepareEvent,  this);
14462                 this.el.on("keypress", this.relay,  this);
14463             }
14464                     this.disabled = false;
14465                 }
14466         },
14467
14468         /**
14469          * Disable this KeyNav
14470          */
14471         disable: function(){
14472                 if(!this.disabled){
14473                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14474                 this.el.un("keydown", this.relay);
14475             }else{
14476                 this.el.un("keydown", this.prepareEvent);
14477                 this.el.un("keypress", this.relay);
14478             }
14479                     this.disabled = true;
14480                 }
14481         }
14482 };/*
14483  * Based on:
14484  * Ext JS Library 1.1.1
14485  * Copyright(c) 2006-2007, Ext JS, LLC.
14486  *
14487  * Originally Released Under LGPL - original licence link has changed is not relivant.
14488  *
14489  * Fork - LGPL
14490  * <script type="text/javascript">
14491  */
14492
14493  
14494 /**
14495  * @class Roo.KeyMap
14496  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14497  * The constructor accepts the same config object as defined by {@link #addBinding}.
14498  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14499  * combination it will call the function with this signature (if the match is a multi-key
14500  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14501  * A KeyMap can also handle a string representation of keys.<br />
14502  * Usage:
14503  <pre><code>
14504 // map one key by key code
14505 var map = new Roo.KeyMap("my-element", {
14506     key: 13, // or Roo.EventObject.ENTER
14507     fn: myHandler,
14508     scope: myObject
14509 });
14510
14511 // map multiple keys to one action by string
14512 var map = new Roo.KeyMap("my-element", {
14513     key: "a\r\n\t",
14514     fn: myHandler,
14515     scope: myObject
14516 });
14517
14518 // map multiple keys to multiple actions by strings and array of codes
14519 var map = new Roo.KeyMap("my-element", [
14520     {
14521         key: [10,13],
14522         fn: function(){ alert("Return was pressed"); }
14523     }, {
14524         key: "abc",
14525         fn: function(){ alert('a, b or c was pressed'); }
14526     }, {
14527         key: "\t",
14528         ctrl:true,
14529         shift:true,
14530         fn: function(){ alert('Control + shift + tab was pressed.'); }
14531     }
14532 ]);
14533 </code></pre>
14534  * <b>Note: A KeyMap starts enabled</b>
14535  * @constructor
14536  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14537  * @param {Object} config The config (see {@link #addBinding})
14538  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14539  */
14540 Roo.KeyMap = function(el, config, eventName){
14541     this.el  = Roo.get(el);
14542     this.eventName = eventName || "keydown";
14543     this.bindings = [];
14544     if(config){
14545         this.addBinding(config);
14546     }
14547     this.enable();
14548 };
14549
14550 Roo.KeyMap.prototype = {
14551     /**
14552      * True to stop the event from bubbling and prevent the default browser action if the
14553      * key was handled by the KeyMap (defaults to false)
14554      * @type Boolean
14555      */
14556     stopEvent : false,
14557
14558     /**
14559      * Add a new binding to this KeyMap. The following config object properties are supported:
14560      * <pre>
14561 Property    Type             Description
14562 ----------  ---------------  ----------------------------------------------------------------------
14563 key         String/Array     A single keycode or an array of keycodes to handle
14564 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14565 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14566 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14567 fn          Function         The function to call when KeyMap finds the expected key combination
14568 scope       Object           The scope of the callback function
14569 </pre>
14570      *
14571      * Usage:
14572      * <pre><code>
14573 // Create a KeyMap
14574 var map = new Roo.KeyMap(document, {
14575     key: Roo.EventObject.ENTER,
14576     fn: handleKey,
14577     scope: this
14578 });
14579
14580 //Add a new binding to the existing KeyMap later
14581 map.addBinding({
14582     key: 'abc',
14583     shift: true,
14584     fn: handleKey,
14585     scope: this
14586 });
14587 </code></pre>
14588      * @param {Object/Array} config A single KeyMap config or an array of configs
14589      */
14590         addBinding : function(config){
14591         if(config instanceof Array){
14592             for(var i = 0, len = config.length; i < len; i++){
14593                 this.addBinding(config[i]);
14594             }
14595             return;
14596         }
14597         var keyCode = config.key,
14598             shift = config.shift, 
14599             ctrl = config.ctrl, 
14600             alt = config.alt,
14601             fn = config.fn,
14602             scope = config.scope;
14603         if(typeof keyCode == "string"){
14604             var ks = [];
14605             var keyString = keyCode.toUpperCase();
14606             for(var j = 0, len = keyString.length; j < len; j++){
14607                 ks.push(keyString.charCodeAt(j));
14608             }
14609             keyCode = ks;
14610         }
14611         var keyArray = keyCode instanceof Array;
14612         var handler = function(e){
14613             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14614                 var k = e.getKey();
14615                 if(keyArray){
14616                     for(var i = 0, len = keyCode.length; i < len; i++){
14617                         if(keyCode[i] == k){
14618                           if(this.stopEvent){
14619                               e.stopEvent();
14620                           }
14621                           fn.call(scope || window, k, e);
14622                           return;
14623                         }
14624                     }
14625                 }else{
14626                     if(k == keyCode){
14627                         if(this.stopEvent){
14628                            e.stopEvent();
14629                         }
14630                         fn.call(scope || window, k, e);
14631                     }
14632                 }
14633             }
14634         };
14635         this.bindings.push(handler);  
14636         },
14637
14638     /**
14639      * Shorthand for adding a single key listener
14640      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14641      * following options:
14642      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14643      * @param {Function} fn The function to call
14644      * @param {Object} scope (optional) The scope of the function
14645      */
14646     on : function(key, fn, scope){
14647         var keyCode, shift, ctrl, alt;
14648         if(typeof key == "object" && !(key instanceof Array)){
14649             keyCode = key.key;
14650             shift = key.shift;
14651             ctrl = key.ctrl;
14652             alt = key.alt;
14653         }else{
14654             keyCode = key;
14655         }
14656         this.addBinding({
14657             key: keyCode,
14658             shift: shift,
14659             ctrl: ctrl,
14660             alt: alt,
14661             fn: fn,
14662             scope: scope
14663         })
14664     },
14665
14666     // private
14667     handleKeyDown : function(e){
14668             if(this.enabled){ //just in case
14669             var b = this.bindings;
14670             for(var i = 0, len = b.length; i < len; i++){
14671                 b[i].call(this, e);
14672             }
14673             }
14674         },
14675         
14676         /**
14677          * Returns true if this KeyMap is enabled
14678          * @return {Boolean} 
14679          */
14680         isEnabled : function(){
14681             return this.enabled;  
14682         },
14683         
14684         /**
14685          * Enables this KeyMap
14686          */
14687         enable: function(){
14688                 if(!this.enabled){
14689                     this.el.on(this.eventName, this.handleKeyDown, this);
14690                     this.enabled = true;
14691                 }
14692         },
14693
14694         /**
14695          * Disable this KeyMap
14696          */
14697         disable: function(){
14698                 if(this.enabled){
14699                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14700                     this.enabled = false;
14701                 }
14702         }
14703 };/*
14704  * Based on:
14705  * Ext JS Library 1.1.1
14706  * Copyright(c) 2006-2007, Ext JS, LLC.
14707  *
14708  * Originally Released Under LGPL - original licence link has changed is not relivant.
14709  *
14710  * Fork - LGPL
14711  * <script type="text/javascript">
14712  */
14713
14714  
14715 /**
14716  * @class Roo.util.TextMetrics
14717  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14718  * wide, in pixels, a given block of text will be.
14719  * @singleton
14720  */
14721 Roo.util.TextMetrics = function(){
14722     var shared;
14723     return {
14724         /**
14725          * Measures the size of the specified text
14726          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14727          * that can affect the size of the rendered text
14728          * @param {String} text The text to measure
14729          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14730          * in order to accurately measure the text height
14731          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14732          */
14733         measure : function(el, text, fixedWidth){
14734             if(!shared){
14735                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14736             }
14737             shared.bind(el);
14738             shared.setFixedWidth(fixedWidth || 'auto');
14739             return shared.getSize(text);
14740         },
14741
14742         /**
14743          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14744          * the overhead of multiple calls to initialize the style properties on each measurement.
14745          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14746          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14747          * in order to accurately measure the text height
14748          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14749          */
14750         createInstance : function(el, fixedWidth){
14751             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14752         }
14753     };
14754 }();
14755
14756  
14757
14758 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14759     var ml = new Roo.Element(document.createElement('div'));
14760     document.body.appendChild(ml.dom);
14761     ml.position('absolute');
14762     ml.setLeftTop(-1000, -1000);
14763     ml.hide();
14764
14765     if(fixedWidth){
14766         ml.setWidth(fixedWidth);
14767     }
14768      
14769     var instance = {
14770         /**
14771          * Returns the size of the specified text based on the internal element's style and width properties
14772          * @memberOf Roo.util.TextMetrics.Instance#
14773          * @param {String} text The text to measure
14774          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14775          */
14776         getSize : function(text){
14777             ml.update(text);
14778             var s = ml.getSize();
14779             ml.update('');
14780             return s;
14781         },
14782
14783         /**
14784          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14785          * that can affect the size of the rendered text
14786          * @memberOf Roo.util.TextMetrics.Instance#
14787          * @param {String/HTMLElement} el The element, dom node or id
14788          */
14789         bind : function(el){
14790             ml.setStyle(
14791                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14792             );
14793         },
14794
14795         /**
14796          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14797          * to set a fixed width in order to accurately measure the text height.
14798          * @memberOf Roo.util.TextMetrics.Instance#
14799          * @param {Number} width The width to set on the element
14800          */
14801         setFixedWidth : function(width){
14802             ml.setWidth(width);
14803         },
14804
14805         /**
14806          * Returns the measured width of the specified text
14807          * @memberOf Roo.util.TextMetrics.Instance#
14808          * @param {String} text The text to measure
14809          * @return {Number} width The width in pixels
14810          */
14811         getWidth : function(text){
14812             ml.dom.style.width = 'auto';
14813             return this.getSize(text).width;
14814         },
14815
14816         /**
14817          * Returns the measured height of the specified text.  For multiline text, be sure to call
14818          * {@link #setFixedWidth} if necessary.
14819          * @memberOf Roo.util.TextMetrics.Instance#
14820          * @param {String} text The text to measure
14821          * @return {Number} height The height in pixels
14822          */
14823         getHeight : function(text){
14824             return this.getSize(text).height;
14825         }
14826     };
14827
14828     instance.bind(bindTo);
14829
14830     return instance;
14831 };
14832
14833 // backwards compat
14834 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14835  * Based on:
14836  * Ext JS Library 1.1.1
14837  * Copyright(c) 2006-2007, Ext JS, LLC.
14838  *
14839  * Originally Released Under LGPL - original licence link has changed is not relivant.
14840  *
14841  * Fork - LGPL
14842  * <script type="text/javascript">
14843  */
14844
14845 /**
14846  * @class Roo.state.Provider
14847  * Abstract base class for state provider implementations. This class provides methods
14848  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14849  * Provider interface.
14850  */
14851 Roo.state.Provider = function(){
14852     /**
14853      * @event statechange
14854      * Fires when a state change occurs.
14855      * @param {Provider} this This state provider
14856      * @param {String} key The state key which was changed
14857      * @param {String} value The encoded value for the state
14858      */
14859     this.addEvents({
14860         "statechange": true
14861     });
14862     this.state = {};
14863     Roo.state.Provider.superclass.constructor.call(this);
14864 };
14865 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14866     /**
14867      * Returns the current value for a key
14868      * @param {String} name The key name
14869      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14870      * @return {Mixed} The state data
14871      */
14872     get : function(name, defaultValue){
14873         return typeof this.state[name] == "undefined" ?
14874             defaultValue : this.state[name];
14875     },
14876     
14877     /**
14878      * Clears a value from the state
14879      * @param {String} name The key name
14880      */
14881     clear : function(name){
14882         delete this.state[name];
14883         this.fireEvent("statechange", this, name, null);
14884     },
14885     
14886     /**
14887      * Sets the value for a key
14888      * @param {String} name The key name
14889      * @param {Mixed} value The value to set
14890      */
14891     set : function(name, value){
14892         this.state[name] = value;
14893         this.fireEvent("statechange", this, name, value);
14894     },
14895     
14896     /**
14897      * Decodes a string previously encoded with {@link #encodeValue}.
14898      * @param {String} value The value to decode
14899      * @return {Mixed} The decoded value
14900      */
14901     decodeValue : function(cookie){
14902         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14903         var matches = re.exec(unescape(cookie));
14904         if(!matches || !matches[1]) {
14905             return; // non state cookie
14906         }
14907         var type = matches[1];
14908         var v = matches[2];
14909         switch(type){
14910             case "n":
14911                 return parseFloat(v);
14912             case "d":
14913                 return new Date(Date.parse(v));
14914             case "b":
14915                 return (v == "1");
14916             case "a":
14917                 var all = [];
14918                 var values = v.split("^");
14919                 for(var i = 0, len = values.length; i < len; i++){
14920                     all.push(this.decodeValue(values[i]));
14921                 }
14922                 return all;
14923            case "o":
14924                 var all = {};
14925                 var values = v.split("^");
14926                 for(var i = 0, len = values.length; i < len; i++){
14927                     var kv = values[i].split("=");
14928                     all[kv[0]] = this.decodeValue(kv[1]);
14929                 }
14930                 return all;
14931            default:
14932                 return v;
14933         }
14934     },
14935     
14936     /**
14937      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14938      * @param {Mixed} value The value to encode
14939      * @return {String} The encoded value
14940      */
14941     encodeValue : function(v){
14942         var enc;
14943         if(typeof v == "number"){
14944             enc = "n:" + v;
14945         }else if(typeof v == "boolean"){
14946             enc = "b:" + (v ? "1" : "0");
14947         }else if(v instanceof Date){
14948             enc = "d:" + v.toGMTString();
14949         }else if(v instanceof Array){
14950             var flat = "";
14951             for(var i = 0, len = v.length; i < len; i++){
14952                 flat += this.encodeValue(v[i]);
14953                 if(i != len-1) {
14954                     flat += "^";
14955                 }
14956             }
14957             enc = "a:" + flat;
14958         }else if(typeof v == "object"){
14959             var flat = "";
14960             for(var key in v){
14961                 if(typeof v[key] != "function"){
14962                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14963                 }
14964             }
14965             enc = "o:" + flat.substring(0, flat.length-1);
14966         }else{
14967             enc = "s:" + v;
14968         }
14969         return escape(enc);        
14970     }
14971 });
14972
14973 /*
14974  * Based on:
14975  * Ext JS Library 1.1.1
14976  * Copyright(c) 2006-2007, Ext JS, LLC.
14977  *
14978  * Originally Released Under LGPL - original licence link has changed is not relivant.
14979  *
14980  * Fork - LGPL
14981  * <script type="text/javascript">
14982  */
14983 /**
14984  * @class Roo.state.Manager
14985  * This is the global state manager. By default all components that are "state aware" check this class
14986  * for state information if you don't pass them a custom state provider. In order for this class
14987  * to be useful, it must be initialized with a provider when your application initializes.
14988  <pre><code>
14989 // in your initialization function
14990 init : function(){
14991    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14992    ...
14993    // supposed you have a {@link Roo.BorderLayout}
14994    var layout = new Roo.BorderLayout(...);
14995    layout.restoreState();
14996    // or a {Roo.BasicDialog}
14997    var dialog = new Roo.BasicDialog(...);
14998    dialog.restoreState();
14999  </code></pre>
15000  * @singleton
15001  */
15002 Roo.state.Manager = function(){
15003     var provider = new Roo.state.Provider();
15004     
15005     return {
15006         /**
15007          * Configures the default state provider for your application
15008          * @param {Provider} stateProvider The state provider to set
15009          */
15010         setProvider : function(stateProvider){
15011             provider = stateProvider;
15012         },
15013         
15014         /**
15015          * Returns the current value for a key
15016          * @param {String} name The key name
15017          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15018          * @return {Mixed} The state data
15019          */
15020         get : function(key, defaultValue){
15021             return provider.get(key, defaultValue);
15022         },
15023         
15024         /**
15025          * Sets the value for a key
15026          * @param {String} name The key name
15027          * @param {Mixed} value The state data
15028          */
15029          set : function(key, value){
15030             provider.set(key, value);
15031         },
15032         
15033         /**
15034          * Clears a value from the state
15035          * @param {String} name The key name
15036          */
15037         clear : function(key){
15038             provider.clear(key);
15039         },
15040         
15041         /**
15042          * Gets the currently configured state provider
15043          * @return {Provider} The state provider
15044          */
15045         getProvider : function(){
15046             return provider;
15047         }
15048     };
15049 }();
15050 /*
15051  * Based on:
15052  * Ext JS Library 1.1.1
15053  * Copyright(c) 2006-2007, Ext JS, LLC.
15054  *
15055  * Originally Released Under LGPL - original licence link has changed is not relivant.
15056  *
15057  * Fork - LGPL
15058  * <script type="text/javascript">
15059  */
15060 /**
15061  * @class Roo.state.CookieProvider
15062  * @extends Roo.state.Provider
15063  * The default Provider implementation which saves state via cookies.
15064  * <br />Usage:
15065  <pre><code>
15066    var cp = new Roo.state.CookieProvider({
15067        path: "/cgi-bin/",
15068        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15069        domain: "roojs.com"
15070    })
15071    Roo.state.Manager.setProvider(cp);
15072  </code></pre>
15073  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15074  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15075  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15076  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15077  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15078  * domain the page is running on including the 'www' like 'www.roojs.com')
15079  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15080  * @constructor
15081  * Create a new CookieProvider
15082  * @param {Object} config The configuration object
15083  */
15084 Roo.state.CookieProvider = function(config){
15085     Roo.state.CookieProvider.superclass.constructor.call(this);
15086     this.path = "/";
15087     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15088     this.domain = null;
15089     this.secure = false;
15090     Roo.apply(this, config);
15091     this.state = this.readCookies();
15092 };
15093
15094 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15095     // private
15096     set : function(name, value){
15097         if(typeof value == "undefined" || value === null){
15098             this.clear(name);
15099             return;
15100         }
15101         this.setCookie(name, value);
15102         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15103     },
15104
15105     // private
15106     clear : function(name){
15107         this.clearCookie(name);
15108         Roo.state.CookieProvider.superclass.clear.call(this, name);
15109     },
15110
15111     // private
15112     readCookies : function(){
15113         var cookies = {};
15114         var c = document.cookie + ";";
15115         var re = /\s?(.*?)=(.*?);/g;
15116         var matches;
15117         while((matches = re.exec(c)) != null){
15118             var name = matches[1];
15119             var value = matches[2];
15120             if(name && name.substring(0,3) == "ys-"){
15121                 cookies[name.substr(3)] = this.decodeValue(value);
15122             }
15123         }
15124         return cookies;
15125     },
15126
15127     // private
15128     setCookie : function(name, value){
15129         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15130            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15131            ((this.path == null) ? "" : ("; path=" + this.path)) +
15132            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15133            ((this.secure == true) ? "; secure" : "");
15134     },
15135
15136     // private
15137     clearCookie : function(name){
15138         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15139            ((this.path == null) ? "" : ("; path=" + this.path)) +
15140            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15141            ((this.secure == true) ? "; secure" : "");
15142     }
15143 });/*
15144  * Based on:
15145  * Ext JS Library 1.1.1
15146  * Copyright(c) 2006-2007, Ext JS, LLC.
15147  *
15148  * Originally Released Under LGPL - original licence link has changed is not relivant.
15149  *
15150  * Fork - LGPL
15151  * <script type="text/javascript">
15152  */
15153  
15154
15155 /**
15156  * @class Roo.ComponentMgr
15157  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15158  * @singleton
15159  */
15160 Roo.ComponentMgr = function(){
15161     var all = new Roo.util.MixedCollection();
15162
15163     return {
15164         /**
15165          * Registers a component.
15166          * @param {Roo.Component} c The component
15167          */
15168         register : function(c){
15169             all.add(c);
15170         },
15171
15172         /**
15173          * Unregisters a component.
15174          * @param {Roo.Component} c The component
15175          */
15176         unregister : function(c){
15177             all.remove(c);
15178         },
15179
15180         /**
15181          * Returns a component by id
15182          * @param {String} id The component id
15183          */
15184         get : function(id){
15185             return all.get(id);
15186         },
15187
15188         /**
15189          * Registers a function that will be called when a specified component is added to ComponentMgr
15190          * @param {String} id The component id
15191          * @param {Funtction} fn The callback function
15192          * @param {Object} scope The scope of the callback
15193          */
15194         onAvailable : function(id, fn, scope){
15195             all.on("add", function(index, o){
15196                 if(o.id == id){
15197                     fn.call(scope || o, o);
15198                     all.un("add", fn, scope);
15199                 }
15200             });
15201         }
15202     };
15203 }();/*
15204  * Based on:
15205  * Ext JS Library 1.1.1
15206  * Copyright(c) 2006-2007, Ext JS, LLC.
15207  *
15208  * Originally Released Under LGPL - original licence link has changed is not relivant.
15209  *
15210  * Fork - LGPL
15211  * <script type="text/javascript">
15212  */
15213  
15214 /**
15215  * @class Roo.Component
15216  * @extends Roo.util.Observable
15217  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15218  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15219  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15220  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15221  * All visual components (widgets) that require rendering into a layout should subclass Component.
15222  * @constructor
15223  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15224  * 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
15225  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15226  */
15227 Roo.Component = function(config){
15228     config = config || {};
15229     if(config.tagName || config.dom || typeof config == "string"){ // element object
15230         config = {el: config, id: config.id || config};
15231     }
15232     this.initialConfig = config;
15233
15234     Roo.apply(this, config);
15235     this.addEvents({
15236         /**
15237          * @event disable
15238          * Fires after the component is disabled.
15239              * @param {Roo.Component} this
15240              */
15241         disable : true,
15242         /**
15243          * @event enable
15244          * Fires after the component is enabled.
15245              * @param {Roo.Component} this
15246              */
15247         enable : true,
15248         /**
15249          * @event beforeshow
15250          * Fires before the component is shown.  Return false to stop the show.
15251              * @param {Roo.Component} this
15252              */
15253         beforeshow : true,
15254         /**
15255          * @event show
15256          * Fires after the component is shown.
15257              * @param {Roo.Component} this
15258              */
15259         show : true,
15260         /**
15261          * @event beforehide
15262          * Fires before the component is hidden. Return false to stop the hide.
15263              * @param {Roo.Component} this
15264              */
15265         beforehide : true,
15266         /**
15267          * @event hide
15268          * Fires after the component is hidden.
15269              * @param {Roo.Component} this
15270              */
15271         hide : true,
15272         /**
15273          * @event beforerender
15274          * Fires before the component is rendered. Return false to stop the render.
15275              * @param {Roo.Component} this
15276              */
15277         beforerender : true,
15278         /**
15279          * @event render
15280          * Fires after the component is rendered.
15281              * @param {Roo.Component} this
15282              */
15283         render : true,
15284         /**
15285          * @event beforedestroy
15286          * Fires before the component is destroyed. Return false to stop the destroy.
15287              * @param {Roo.Component} this
15288              */
15289         beforedestroy : true,
15290         /**
15291          * @event destroy
15292          * Fires after the component is destroyed.
15293              * @param {Roo.Component} this
15294              */
15295         destroy : true
15296     });
15297     if(!this.id){
15298         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15299     }
15300     Roo.ComponentMgr.register(this);
15301     Roo.Component.superclass.constructor.call(this);
15302     this.initComponent();
15303     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15304         this.render(this.renderTo);
15305         delete this.renderTo;
15306     }
15307 };
15308
15309 /** @private */
15310 Roo.Component.AUTO_ID = 1000;
15311
15312 Roo.extend(Roo.Component, Roo.util.Observable, {
15313     /**
15314      * @scope Roo.Component.prototype
15315      * @type {Boolean}
15316      * true if this component is hidden. Read-only.
15317      */
15318     hidden : false,
15319     /**
15320      * @type {Boolean}
15321      * true if this component is disabled. Read-only.
15322      */
15323     disabled : false,
15324     /**
15325      * @type {Boolean}
15326      * true if this component has been rendered. Read-only.
15327      */
15328     rendered : false,
15329     
15330     /** @cfg {String} disableClass
15331      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15332      */
15333     disabledClass : "x-item-disabled",
15334         /** @cfg {Boolean} allowDomMove
15335          * Whether the component can move the Dom node when rendering (defaults to true).
15336          */
15337     allowDomMove : true,
15338     /** @cfg {String} hideMode (display|visibility)
15339      * How this component should hidden. Supported values are
15340      * "visibility" (css visibility), "offsets" (negative offset position) and
15341      * "display" (css display) - defaults to "display".
15342      */
15343     hideMode: 'display',
15344
15345     /** @private */
15346     ctype : "Roo.Component",
15347
15348     /**
15349      * @cfg {String} actionMode 
15350      * which property holds the element that used for  hide() / show() / disable() / enable()
15351      * default is 'el' 
15352      */
15353     actionMode : "el",
15354
15355     /** @private */
15356     getActionEl : function(){
15357         return this[this.actionMode];
15358     },
15359
15360     initComponent : Roo.emptyFn,
15361     /**
15362      * If this is a lazy rendering component, render it to its container element.
15363      * @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.
15364      */
15365     render : function(container, position){
15366         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15367             if(!container && this.el){
15368                 this.el = Roo.get(this.el);
15369                 container = this.el.dom.parentNode;
15370                 this.allowDomMove = false;
15371             }
15372             this.container = Roo.get(container);
15373             this.rendered = true;
15374             if(position !== undefined){
15375                 if(typeof position == 'number'){
15376                     position = this.container.dom.childNodes[position];
15377                 }else{
15378                     position = Roo.getDom(position);
15379                 }
15380             }
15381             this.onRender(this.container, position || null);
15382             if(this.cls){
15383                 this.el.addClass(this.cls);
15384                 delete this.cls;
15385             }
15386             if(this.style){
15387                 this.el.applyStyles(this.style);
15388                 delete this.style;
15389             }
15390             this.fireEvent("render", this);
15391             this.afterRender(this.container);
15392             if(this.hidden){
15393                 this.hide();
15394             }
15395             if(this.disabled){
15396                 this.disable();
15397             }
15398         }
15399         return this;
15400     },
15401
15402     /** @private */
15403     // default function is not really useful
15404     onRender : function(ct, position){
15405         if(this.el){
15406             this.el = Roo.get(this.el);
15407             if(this.allowDomMove !== false){
15408                 ct.dom.insertBefore(this.el.dom, position);
15409             }
15410         }
15411     },
15412
15413     /** @private */
15414     getAutoCreate : function(){
15415         var cfg = typeof this.autoCreate == "object" ?
15416                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15417         if(this.id && !cfg.id){
15418             cfg.id = this.id;
15419         }
15420         return cfg;
15421     },
15422
15423     /** @private */
15424     afterRender : Roo.emptyFn,
15425
15426     /**
15427      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15428      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15429      */
15430     destroy : function(){
15431         if(this.fireEvent("beforedestroy", this) !== false){
15432             this.purgeListeners();
15433             this.beforeDestroy();
15434             if(this.rendered){
15435                 this.el.removeAllListeners();
15436                 this.el.remove();
15437                 if(this.actionMode == "container"){
15438                     this.container.remove();
15439                 }
15440             }
15441             this.onDestroy();
15442             Roo.ComponentMgr.unregister(this);
15443             this.fireEvent("destroy", this);
15444         }
15445     },
15446
15447         /** @private */
15448     beforeDestroy : function(){
15449
15450     },
15451
15452         /** @private */
15453         onDestroy : function(){
15454
15455     },
15456
15457     /**
15458      * Returns the underlying {@link Roo.Element}.
15459      * @return {Roo.Element} The element
15460      */
15461     getEl : function(){
15462         return this.el;
15463     },
15464
15465     /**
15466      * Returns the id of this component.
15467      * @return {String}
15468      */
15469     getId : function(){
15470         return this.id;
15471     },
15472
15473     /**
15474      * Try to focus this component.
15475      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15476      * @return {Roo.Component} this
15477      */
15478     focus : function(selectText){
15479         if(this.rendered){
15480             this.el.focus();
15481             if(selectText === true){
15482                 this.el.dom.select();
15483             }
15484         }
15485         return this;
15486     },
15487
15488     /** @private */
15489     blur : function(){
15490         if(this.rendered){
15491             this.el.blur();
15492         }
15493         return this;
15494     },
15495
15496     /**
15497      * Disable this component.
15498      * @return {Roo.Component} this
15499      */
15500     disable : function(){
15501         if(this.rendered){
15502             this.onDisable();
15503         }
15504         this.disabled = true;
15505         this.fireEvent("disable", this);
15506         return this;
15507     },
15508
15509         // private
15510     onDisable : function(){
15511         this.getActionEl().addClass(this.disabledClass);
15512         this.el.dom.disabled = true;
15513     },
15514
15515     /**
15516      * Enable this component.
15517      * @return {Roo.Component} this
15518      */
15519     enable : function(){
15520         if(this.rendered){
15521             this.onEnable();
15522         }
15523         this.disabled = false;
15524         this.fireEvent("enable", this);
15525         return this;
15526     },
15527
15528         // private
15529     onEnable : function(){
15530         this.getActionEl().removeClass(this.disabledClass);
15531         this.el.dom.disabled = false;
15532     },
15533
15534     /**
15535      * Convenience function for setting disabled/enabled by boolean.
15536      * @param {Boolean} disabled
15537      */
15538     setDisabled : function(disabled){
15539         this[disabled ? "disable" : "enable"]();
15540     },
15541
15542     /**
15543      * Show this component.
15544      * @return {Roo.Component} this
15545      */
15546     show: function(){
15547         if(this.fireEvent("beforeshow", this) !== false){
15548             this.hidden = false;
15549             if(this.rendered){
15550                 this.onShow();
15551             }
15552             this.fireEvent("show", this);
15553         }
15554         return this;
15555     },
15556
15557     // private
15558     onShow : function(){
15559         var ae = this.getActionEl();
15560         if(this.hideMode == 'visibility'){
15561             ae.dom.style.visibility = "visible";
15562         }else if(this.hideMode == 'offsets'){
15563             ae.removeClass('x-hidden');
15564         }else{
15565             ae.dom.style.display = "";
15566         }
15567     },
15568
15569     /**
15570      * Hide this component.
15571      * @return {Roo.Component} this
15572      */
15573     hide: function(){
15574         if(this.fireEvent("beforehide", this) !== false){
15575             this.hidden = true;
15576             if(this.rendered){
15577                 this.onHide();
15578             }
15579             this.fireEvent("hide", this);
15580         }
15581         return this;
15582     },
15583
15584     // private
15585     onHide : function(){
15586         var ae = this.getActionEl();
15587         if(this.hideMode == 'visibility'){
15588             ae.dom.style.visibility = "hidden";
15589         }else if(this.hideMode == 'offsets'){
15590             ae.addClass('x-hidden');
15591         }else{
15592             ae.dom.style.display = "none";
15593         }
15594     },
15595
15596     /**
15597      * Convenience function to hide or show this component by boolean.
15598      * @param {Boolean} visible True to show, false to hide
15599      * @return {Roo.Component} this
15600      */
15601     setVisible: function(visible){
15602         if(visible) {
15603             this.show();
15604         }else{
15605             this.hide();
15606         }
15607         return this;
15608     },
15609
15610     /**
15611      * Returns true if this component is visible.
15612      */
15613     isVisible : function(){
15614         return this.getActionEl().isVisible();
15615     },
15616
15617     cloneConfig : function(overrides){
15618         overrides = overrides || {};
15619         var id = overrides.id || Roo.id();
15620         var cfg = Roo.applyIf(overrides, this.initialConfig);
15621         cfg.id = id; // prevent dup id
15622         return new this.constructor(cfg);
15623     }
15624 });/*
15625  * Based on:
15626  * Ext JS Library 1.1.1
15627  * Copyright(c) 2006-2007, Ext JS, LLC.
15628  *
15629  * Originally Released Under LGPL - original licence link has changed is not relivant.
15630  *
15631  * Fork - LGPL
15632  * <script type="text/javascript">
15633  */
15634
15635 /**
15636  * @class Roo.BoxComponent
15637  * @extends Roo.Component
15638  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15639  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15640  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15641  * layout containers.
15642  * @constructor
15643  * @param {Roo.Element/String/Object} config The configuration options.
15644  */
15645 Roo.BoxComponent = function(config){
15646     Roo.Component.call(this, config);
15647     this.addEvents({
15648         /**
15649          * @event resize
15650          * Fires after the component is resized.
15651              * @param {Roo.Component} this
15652              * @param {Number} adjWidth The box-adjusted width that was set
15653              * @param {Number} adjHeight The box-adjusted height that was set
15654              * @param {Number} rawWidth The width that was originally specified
15655              * @param {Number} rawHeight The height that was originally specified
15656              */
15657         resize : true,
15658         /**
15659          * @event move
15660          * Fires after the component is moved.
15661              * @param {Roo.Component} this
15662              * @param {Number} x The new x position
15663              * @param {Number} y The new y position
15664              */
15665         move : true
15666     });
15667 };
15668
15669 Roo.extend(Roo.BoxComponent, Roo.Component, {
15670     // private, set in afterRender to signify that the component has been rendered
15671     boxReady : false,
15672     // private, used to defer height settings to subclasses
15673     deferHeight: false,
15674     /** @cfg {Number} width
15675      * width (optional) size of component
15676      */
15677      /** @cfg {Number} height
15678      * height (optional) size of component
15679      */
15680      
15681     /**
15682      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15683      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15684      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15685      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15686      * @return {Roo.BoxComponent} this
15687      */
15688     setSize : function(w, h){
15689         // support for standard size objects
15690         if(typeof w == 'object'){
15691             h = w.height;
15692             w = w.width;
15693         }
15694         // not rendered
15695         if(!this.boxReady){
15696             this.width = w;
15697             this.height = h;
15698             return this;
15699         }
15700
15701         // prevent recalcs when not needed
15702         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15703             return this;
15704         }
15705         this.lastSize = {width: w, height: h};
15706
15707         var adj = this.adjustSize(w, h);
15708         var aw = adj.width, ah = adj.height;
15709         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15710             var rz = this.getResizeEl();
15711             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15712                 rz.setSize(aw, ah);
15713             }else if(!this.deferHeight && ah !== undefined){
15714                 rz.setHeight(ah);
15715             }else if(aw !== undefined){
15716                 rz.setWidth(aw);
15717             }
15718             this.onResize(aw, ah, w, h);
15719             this.fireEvent('resize', this, aw, ah, w, h);
15720         }
15721         return this;
15722     },
15723
15724     /**
15725      * Gets the current size of the component's underlying element.
15726      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15727      */
15728     getSize : function(){
15729         return this.el.getSize();
15730     },
15731
15732     /**
15733      * Gets the current XY position of the component's underlying element.
15734      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15735      * @return {Array} The XY position of the element (e.g., [100, 200])
15736      */
15737     getPosition : function(local){
15738         if(local === true){
15739             return [this.el.getLeft(true), this.el.getTop(true)];
15740         }
15741         return this.xy || this.el.getXY();
15742     },
15743
15744     /**
15745      * Gets the current box measurements of the component's underlying element.
15746      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15747      * @returns {Object} box An object in the format {x, y, width, height}
15748      */
15749     getBox : function(local){
15750         var s = this.el.getSize();
15751         if(local){
15752             s.x = this.el.getLeft(true);
15753             s.y = this.el.getTop(true);
15754         }else{
15755             var xy = this.xy || this.el.getXY();
15756             s.x = xy[0];
15757             s.y = xy[1];
15758         }
15759         return s;
15760     },
15761
15762     /**
15763      * Sets the current box measurements of the component's underlying element.
15764      * @param {Object} box An object in the format {x, y, width, height}
15765      * @returns {Roo.BoxComponent} this
15766      */
15767     updateBox : function(box){
15768         this.setSize(box.width, box.height);
15769         this.setPagePosition(box.x, box.y);
15770         return this;
15771     },
15772
15773     // protected
15774     getResizeEl : function(){
15775         return this.resizeEl || this.el;
15776     },
15777
15778     // protected
15779     getPositionEl : function(){
15780         return this.positionEl || this.el;
15781     },
15782
15783     /**
15784      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15785      * This method fires the move event.
15786      * @param {Number} left The new left
15787      * @param {Number} top The new top
15788      * @returns {Roo.BoxComponent} this
15789      */
15790     setPosition : function(x, y){
15791         this.x = x;
15792         this.y = y;
15793         if(!this.boxReady){
15794             return this;
15795         }
15796         var adj = this.adjustPosition(x, y);
15797         var ax = adj.x, ay = adj.y;
15798
15799         var el = this.getPositionEl();
15800         if(ax !== undefined || ay !== undefined){
15801             if(ax !== undefined && ay !== undefined){
15802                 el.setLeftTop(ax, ay);
15803             }else if(ax !== undefined){
15804                 el.setLeft(ax);
15805             }else if(ay !== undefined){
15806                 el.setTop(ay);
15807             }
15808             this.onPosition(ax, ay);
15809             this.fireEvent('move', this, ax, ay);
15810         }
15811         return this;
15812     },
15813
15814     /**
15815      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15816      * This method fires the move event.
15817      * @param {Number} x The new x position
15818      * @param {Number} y The new y position
15819      * @returns {Roo.BoxComponent} this
15820      */
15821     setPagePosition : function(x, y){
15822         this.pageX = x;
15823         this.pageY = y;
15824         if(!this.boxReady){
15825             return;
15826         }
15827         if(x === undefined || y === undefined){ // cannot translate undefined points
15828             return;
15829         }
15830         var p = this.el.translatePoints(x, y);
15831         this.setPosition(p.left, p.top);
15832         return this;
15833     },
15834
15835     // private
15836     onRender : function(ct, position){
15837         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15838         if(this.resizeEl){
15839             this.resizeEl = Roo.get(this.resizeEl);
15840         }
15841         if(this.positionEl){
15842             this.positionEl = Roo.get(this.positionEl);
15843         }
15844     },
15845
15846     // private
15847     afterRender : function(){
15848         Roo.BoxComponent.superclass.afterRender.call(this);
15849         this.boxReady = true;
15850         this.setSize(this.width, this.height);
15851         if(this.x || this.y){
15852             this.setPosition(this.x, this.y);
15853         }
15854         if(this.pageX || this.pageY){
15855             this.setPagePosition(this.pageX, this.pageY);
15856         }
15857     },
15858
15859     /**
15860      * Force the component's size to recalculate based on the underlying element's current height and width.
15861      * @returns {Roo.BoxComponent} this
15862      */
15863     syncSize : function(){
15864         delete this.lastSize;
15865         this.setSize(this.el.getWidth(), this.el.getHeight());
15866         return this;
15867     },
15868
15869     /**
15870      * Called after the component is resized, this method is empty by default but can be implemented by any
15871      * subclass that needs to perform custom logic after a resize occurs.
15872      * @param {Number} adjWidth The box-adjusted width that was set
15873      * @param {Number} adjHeight The box-adjusted height that was set
15874      * @param {Number} rawWidth The width that was originally specified
15875      * @param {Number} rawHeight The height that was originally specified
15876      */
15877     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15878
15879     },
15880
15881     /**
15882      * Called after the component is moved, this method is empty by default but can be implemented by any
15883      * subclass that needs to perform custom logic after a move occurs.
15884      * @param {Number} x The new x position
15885      * @param {Number} y The new y position
15886      */
15887     onPosition : function(x, y){
15888
15889     },
15890
15891     // private
15892     adjustSize : function(w, h){
15893         if(this.autoWidth){
15894             w = 'auto';
15895         }
15896         if(this.autoHeight){
15897             h = 'auto';
15898         }
15899         return {width : w, height: h};
15900     },
15901
15902     // private
15903     adjustPosition : function(x, y){
15904         return {x : x, y: y};
15905     }
15906 });/*
15907  * Original code for Roojs - LGPL
15908  * <script type="text/javascript">
15909  */
15910  
15911 /**
15912  * @class Roo.XComponent
15913  * A delayed Element creator...
15914  * Or a way to group chunks of interface together.
15915  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15916  *  used in conjunction with XComponent.build() it will create an instance of each element,
15917  *  then call addxtype() to build the User interface.
15918  * 
15919  * Mypart.xyx = new Roo.XComponent({
15920
15921     parent : 'Mypart.xyz', // empty == document.element.!!
15922     order : '001',
15923     name : 'xxxx'
15924     region : 'xxxx'
15925     disabled : function() {} 
15926      
15927     tree : function() { // return an tree of xtype declared components
15928         var MODULE = this;
15929         return 
15930         {
15931             xtype : 'NestedLayoutPanel',
15932             // technicall
15933         }
15934      ]
15935  *})
15936  *
15937  *
15938  * It can be used to build a big heiracy, with parent etc.
15939  * or you can just use this to render a single compoent to a dom element
15940  * MYPART.render(Roo.Element | String(id) | dom_element )
15941  *
15942  *
15943  * Usage patterns.
15944  *
15945  * Classic Roo
15946  *
15947  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15948  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15949  *
15950  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15951  *
15952  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15953  * - if mulitple topModules exist, the last one is defined as the top module.
15954  *
15955  * Embeded Roo
15956  * 
15957  * When the top level or multiple modules are to embedded into a existing HTML page,
15958  * the parent element can container '#id' of the element where the module will be drawn.
15959  *
15960  * Bootstrap Roo
15961  *
15962  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15963  * it relies more on a include mechanism, where sub modules are included into an outer page.
15964  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15965  * 
15966  * Bootstrap Roo Included elements
15967  *
15968  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15969  * hence confusing the component builder as it thinks there are multiple top level elements. 
15970  *
15971  * 
15972  * 
15973  * @extends Roo.util.Observable
15974  * @constructor
15975  * @param cfg {Object} configuration of component
15976  * 
15977  */
15978 Roo.XComponent = function(cfg) {
15979     Roo.apply(this, cfg);
15980     this.addEvents({ 
15981         /**
15982              * @event built
15983              * Fires when this the componnt is built
15984              * @param {Roo.XComponent} c the component
15985              */
15986         'built' : true
15987         
15988     });
15989     this.region = this.region || 'center'; // default..
15990     Roo.XComponent.register(this);
15991     this.modules = false;
15992     this.el = false; // where the layout goes..
15993     
15994     
15995 }
15996 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15997     /**
15998      * @property el
15999      * The created element (with Roo.factory())
16000      * @type {Roo.Layout}
16001      */
16002     el  : false,
16003     
16004     /**
16005      * @property el
16006      * for BC  - use el in new code
16007      * @type {Roo.Layout}
16008      */
16009     panel : false,
16010     
16011     /**
16012      * @property layout
16013      * for BC  - use el in new code
16014      * @type {Roo.Layout}
16015      */
16016     layout : false,
16017     
16018      /**
16019      * @cfg {Function|boolean} disabled
16020      * If this module is disabled by some rule, return true from the funtion
16021      */
16022     disabled : false,
16023     
16024     /**
16025      * @cfg {String} parent 
16026      * Name of parent element which it get xtype added to..
16027      */
16028     parent: false,
16029     
16030     /**
16031      * @cfg {String} order
16032      * Used to set the order in which elements are created (usefull for multiple tabs)
16033      */
16034     
16035     order : false,
16036     /**
16037      * @cfg {String} name
16038      * String to display while loading.
16039      */
16040     name : false,
16041     /**
16042      * @cfg {String} region
16043      * Region to render component to (defaults to center)
16044      */
16045     region : 'center',
16046     
16047     /**
16048      * @cfg {Array} items
16049      * A single item array - the first element is the root of the tree..
16050      * It's done this way to stay compatible with the Xtype system...
16051      */
16052     items : false,
16053     
16054     /**
16055      * @property _tree
16056      * The method that retuns the tree of parts that make up this compoennt 
16057      * @type {function}
16058      */
16059     _tree  : false,
16060     
16061      /**
16062      * render
16063      * render element to dom or tree
16064      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16065      */
16066     
16067     render : function(el)
16068     {
16069         
16070         el = el || false;
16071         var hp = this.parent ? 1 : 0;
16072         Roo.debug &&  Roo.log(this);
16073         
16074         var tree = this._tree ? this._tree() : this.tree();
16075
16076         
16077         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16078             // if parent is a '#.....' string, then let's use that..
16079             var ename = this.parent.substr(1);
16080             this.parent = false;
16081             Roo.debug && Roo.log(ename);
16082             switch (ename) {
16083                 case 'bootstrap-body':
16084                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16085                         // this is the BorderLayout standard?
16086                        this.parent = { el : true };
16087                        break;
16088                     }
16089                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16090                         // need to insert stuff...
16091                         this.parent =  {
16092                              el : new Roo.bootstrap.layout.Border({
16093                                  el : document.body, 
16094                      
16095                                  center: {
16096                                     titlebar: false,
16097                                     autoScroll:false,
16098                                     closeOnTab: true,
16099                                     tabPosition: 'top',
16100                                       //resizeTabs: true,
16101                                     alwaysShowTabs: true,
16102                                     hideTabs: false
16103                                      //minTabWidth: 140
16104                                  }
16105                              })
16106                         
16107                          };
16108                          break;
16109                     }
16110                          
16111                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16112                         this.parent = { el :  new  Roo.bootstrap.Body() };
16113                         Roo.debug && Roo.log("setting el to doc body");
16114                          
16115                     } else {
16116                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16117                     }
16118                     break;
16119                 case 'bootstrap':
16120                     this.parent = { el : true};
16121                     // fall through
16122                 default:
16123                     el = Roo.get(ename);
16124                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16125                         this.parent = { el : true};
16126                     }
16127                     
16128                     break;
16129             }
16130                 
16131             
16132             if (!el && !this.parent) {
16133                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16134                 return;
16135             }
16136         }
16137         
16138         Roo.debug && Roo.log("EL:");
16139         Roo.debug && Roo.log(el);
16140         Roo.debug && Roo.log("this.parent.el:");
16141         Roo.debug && Roo.log(this.parent.el);
16142         
16143
16144         // altertive root elements ??? - we need a better way to indicate these.
16145         var is_alt = Roo.XComponent.is_alt ||
16146                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16147                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16148                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16149         
16150         
16151         
16152         if (!this.parent && is_alt) {
16153             //el = Roo.get(document.body);
16154             this.parent = { el : true };
16155         }
16156             
16157             
16158         
16159         if (!this.parent) {
16160             
16161             Roo.debug && Roo.log("no parent - creating one");
16162             
16163             el = el ? Roo.get(el) : false;      
16164             
16165             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16166                 
16167                 this.parent =  {
16168                     el : new Roo.bootstrap.layout.Border({
16169                         el: el || document.body,
16170                     
16171                         center: {
16172                             titlebar: false,
16173                             autoScroll:false,
16174                             closeOnTab: true,
16175                             tabPosition: 'top',
16176                              //resizeTabs: true,
16177                             alwaysShowTabs: false,
16178                             hideTabs: true,
16179                             minTabWidth: 140,
16180                             overflow: 'visible'
16181                          }
16182                      })
16183                 };
16184             } else {
16185             
16186                 // it's a top level one..
16187                 this.parent =  {
16188                     el : new Roo.BorderLayout(el || document.body, {
16189                         center: {
16190                             titlebar: false,
16191                             autoScroll:false,
16192                             closeOnTab: true,
16193                             tabPosition: 'top',
16194                              //resizeTabs: true,
16195                             alwaysShowTabs: el && hp? false :  true,
16196                             hideTabs: el || !hp ? true :  false,
16197                             minTabWidth: 140
16198                          }
16199                     })
16200                 };
16201             }
16202         }
16203         
16204         if (!this.parent.el) {
16205                 // probably an old style ctor, which has been disabled.
16206                 return;
16207
16208         }
16209                 // The 'tree' method is  '_tree now' 
16210             
16211         tree.region = tree.region || this.region;
16212         var is_body = false;
16213         if (this.parent.el === true) {
16214             // bootstrap... - body..
16215             if (el) {
16216                 tree.el = el;
16217             }
16218             this.parent.el = Roo.factory(tree);
16219             is_body = true;
16220         }
16221         
16222         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16223         this.fireEvent('built', this);
16224         
16225         this.panel = this.el;
16226         this.layout = this.panel.layout;
16227         this.parentLayout = this.parent.layout  || false;  
16228          
16229     }
16230     
16231 });
16232
16233 Roo.apply(Roo.XComponent, {
16234     /**
16235      * @property  hideProgress
16236      * true to disable the building progress bar.. usefull on single page renders.
16237      * @type Boolean
16238      */
16239     hideProgress : false,
16240     /**
16241      * @property  buildCompleted
16242      * True when the builder has completed building the interface.
16243      * @type Boolean
16244      */
16245     buildCompleted : false,
16246      
16247     /**
16248      * @property  topModule
16249      * the upper most module - uses document.element as it's constructor.
16250      * @type Object
16251      */
16252      
16253     topModule  : false,
16254       
16255     /**
16256      * @property  modules
16257      * array of modules to be created by registration system.
16258      * @type {Array} of Roo.XComponent
16259      */
16260     
16261     modules : [],
16262     /**
16263      * @property  elmodules
16264      * array of modules to be created by which use #ID 
16265      * @type {Array} of Roo.XComponent
16266      */
16267      
16268     elmodules : [],
16269
16270      /**
16271      * @property  is_alt
16272      * Is an alternative Root - normally used by bootstrap or other systems,
16273      *    where the top element in the tree can wrap 'body' 
16274      * @type {boolean}  (default false)
16275      */
16276      
16277     is_alt : false,
16278     /**
16279      * @property  build_from_html
16280      * Build elements from html - used by bootstrap HTML stuff 
16281      *    - this is cleared after build is completed
16282      * @type {boolean}    (default false)
16283      */
16284      
16285     build_from_html : false,
16286     /**
16287      * Register components to be built later.
16288      *
16289      * This solves the following issues
16290      * - Building is not done on page load, but after an authentication process has occured.
16291      * - Interface elements are registered on page load
16292      * - Parent Interface elements may not be loaded before child, so this handles that..
16293      * 
16294      *
16295      * example:
16296      * 
16297      * MyApp.register({
16298           order : '000001',
16299           module : 'Pman.Tab.projectMgr',
16300           region : 'center',
16301           parent : 'Pman.layout',
16302           disabled : false,  // or use a function..
16303         })
16304      
16305      * * @param {Object} details about module
16306      */
16307     register : function(obj) {
16308                 
16309         Roo.XComponent.event.fireEvent('register', obj);
16310         switch(typeof(obj.disabled) ) {
16311                 
16312             case 'undefined':
16313                 break;
16314             
16315             case 'function':
16316                 if ( obj.disabled() ) {
16317                         return;
16318                 }
16319                 break;
16320             
16321             default:
16322                 if (obj.disabled) {
16323                         return;
16324                 }
16325                 break;
16326         }
16327                 
16328         this.modules.push(obj);
16329          
16330     },
16331     /**
16332      * convert a string to an object..
16333      * eg. 'AAA.BBB' -> finds AAA.BBB
16334
16335      */
16336     
16337     toObject : function(str)
16338     {
16339         if (!str || typeof(str) == 'object') {
16340             return str;
16341         }
16342         if (str.substring(0,1) == '#') {
16343             return str;
16344         }
16345
16346         var ar = str.split('.');
16347         var rt, o;
16348         rt = ar.shift();
16349             /** eval:var:o */
16350         try {
16351             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16352         } catch (e) {
16353             throw "Module not found : " + str;
16354         }
16355         
16356         if (o === false) {
16357             throw "Module not found : " + str;
16358         }
16359         Roo.each(ar, function(e) {
16360             if (typeof(o[e]) == 'undefined') {
16361                 throw "Module not found : " + str;
16362             }
16363             o = o[e];
16364         });
16365         
16366         return o;
16367         
16368     },
16369     
16370     
16371     /**
16372      * move modules into their correct place in the tree..
16373      * 
16374      */
16375     preBuild : function ()
16376     {
16377         var _t = this;
16378         Roo.each(this.modules , function (obj)
16379         {
16380             Roo.XComponent.event.fireEvent('beforebuild', obj);
16381             
16382             var opar = obj.parent;
16383             try { 
16384                 obj.parent = this.toObject(opar);
16385             } catch(e) {
16386                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16387                 return;
16388             }
16389             
16390             if (!obj.parent) {
16391                 Roo.debug && Roo.log("GOT top level module");
16392                 Roo.debug && Roo.log(obj);
16393                 obj.modules = new Roo.util.MixedCollection(false, 
16394                     function(o) { return o.order + '' }
16395                 );
16396                 this.topModule = obj;
16397                 return;
16398             }
16399                         // parent is a string (usually a dom element name..)
16400             if (typeof(obj.parent) == 'string') {
16401                 this.elmodules.push(obj);
16402                 return;
16403             }
16404             if (obj.parent.constructor != Roo.XComponent) {
16405                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16406             }
16407             if (!obj.parent.modules) {
16408                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16409                     function(o) { return o.order + '' }
16410                 );
16411             }
16412             if (obj.parent.disabled) {
16413                 obj.disabled = true;
16414             }
16415             obj.parent.modules.add(obj);
16416         }, this);
16417     },
16418     
16419      /**
16420      * make a list of modules to build.
16421      * @return {Array} list of modules. 
16422      */ 
16423     
16424     buildOrder : function()
16425     {
16426         var _this = this;
16427         var cmp = function(a,b) {   
16428             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16429         };
16430         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16431             throw "No top level modules to build";
16432         }
16433         
16434         // make a flat list in order of modules to build.
16435         var mods = this.topModule ? [ this.topModule ] : [];
16436                 
16437         
16438         // elmodules (is a list of DOM based modules )
16439         Roo.each(this.elmodules, function(e) {
16440             mods.push(e);
16441             if (!this.topModule &&
16442                 typeof(e.parent) == 'string' &&
16443                 e.parent.substring(0,1) == '#' &&
16444                 Roo.get(e.parent.substr(1))
16445                ) {
16446                 
16447                 _this.topModule = e;
16448             }
16449             
16450         });
16451
16452         
16453         // add modules to their parents..
16454         var addMod = function(m) {
16455             Roo.debug && Roo.log("build Order: add: " + m.name);
16456                 
16457             mods.push(m);
16458             if (m.modules && !m.disabled) {
16459                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16460                 m.modules.keySort('ASC',  cmp );
16461                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16462     
16463                 m.modules.each(addMod);
16464             } else {
16465                 Roo.debug && Roo.log("build Order: no child modules");
16466             }
16467             // not sure if this is used any more..
16468             if (m.finalize) {
16469                 m.finalize.name = m.name + " (clean up) ";
16470                 mods.push(m.finalize);
16471             }
16472             
16473         }
16474         if (this.topModule && this.topModule.modules) { 
16475             this.topModule.modules.keySort('ASC',  cmp );
16476             this.topModule.modules.each(addMod);
16477         } 
16478         return mods;
16479     },
16480     
16481      /**
16482      * Build the registered modules.
16483      * @param {Object} parent element.
16484      * @param {Function} optional method to call after module has been added.
16485      * 
16486      */ 
16487    
16488     build : function(opts) 
16489     {
16490         
16491         if (typeof(opts) != 'undefined') {
16492             Roo.apply(this,opts);
16493         }
16494         
16495         this.preBuild();
16496         var mods = this.buildOrder();
16497       
16498         //this.allmods = mods;
16499         //Roo.debug && Roo.log(mods);
16500         //return;
16501         if (!mods.length) { // should not happen
16502             throw "NO modules!!!";
16503         }
16504         
16505         
16506         var msg = "Building Interface...";
16507         // flash it up as modal - so we store the mask!?
16508         if (!this.hideProgress && Roo.MessageBox) {
16509             Roo.MessageBox.show({ title: 'loading' });
16510             Roo.MessageBox.show({
16511                title: "Please wait...",
16512                msg: msg,
16513                width:450,
16514                progress:true,
16515                closable:false,
16516                modal: false
16517               
16518             });
16519         }
16520         var total = mods.length;
16521         
16522         var _this = this;
16523         var progressRun = function() {
16524             if (!mods.length) {
16525                 Roo.debug && Roo.log('hide?');
16526                 if (!this.hideProgress && Roo.MessageBox) {
16527                     Roo.MessageBox.hide();
16528                 }
16529                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16530                 
16531                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16532                 
16533                 // THE END...
16534                 return false;   
16535             }
16536             
16537             var m = mods.shift();
16538             
16539             
16540             Roo.debug && Roo.log(m);
16541             // not sure if this is supported any more.. - modules that are are just function
16542             if (typeof(m) == 'function') { 
16543                 m.call(this);
16544                 return progressRun.defer(10, _this);
16545             } 
16546             
16547             
16548             msg = "Building Interface " + (total  - mods.length) + 
16549                     " of " + total + 
16550                     (m.name ? (' - ' + m.name) : '');
16551                         Roo.debug && Roo.log(msg);
16552             if (!_this.hideProgress &&  Roo.MessageBox) { 
16553                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16554             }
16555             
16556          
16557             // is the module disabled?
16558             var disabled = (typeof(m.disabled) == 'function') ?
16559                 m.disabled.call(m.module.disabled) : m.disabled;    
16560             
16561             
16562             if (disabled) {
16563                 return progressRun(); // we do not update the display!
16564             }
16565             
16566             // now build 
16567             
16568                         
16569                         
16570             m.render();
16571             // it's 10 on top level, and 1 on others??? why...
16572             return progressRun.defer(10, _this);
16573              
16574         }
16575         progressRun.defer(1, _this);
16576      
16577         
16578         
16579     },
16580         
16581         
16582         /**
16583          * Event Object.
16584          *
16585          *
16586          */
16587         event: false, 
16588     /**
16589          * wrapper for event.on - aliased later..  
16590          * Typically use to register a event handler for register:
16591          *
16592          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16593          *
16594          */
16595     on : false
16596    
16597     
16598     
16599 });
16600
16601 Roo.XComponent.event = new Roo.util.Observable({
16602                 events : { 
16603                         /**
16604                          * @event register
16605                          * Fires when an Component is registered,
16606                          * set the disable property on the Component to stop registration.
16607                          * @param {Roo.XComponent} c the component being registerd.
16608                          * 
16609                          */
16610                         'register' : true,
16611             /**
16612                          * @event beforebuild
16613                          * Fires before each Component is built
16614                          * can be used to apply permissions.
16615                          * @param {Roo.XComponent} c the component being registerd.
16616                          * 
16617                          */
16618                         'beforebuild' : true,
16619                         /**
16620                          * @event buildcomplete
16621                          * Fires on the top level element when all elements have been built
16622                          * @param {Roo.XComponent} the top level component.
16623                          */
16624                         'buildcomplete' : true
16625                         
16626                 }
16627 });
16628
16629 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16630  //
16631  /**
16632  * marked - a markdown parser
16633  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16634  * https://github.com/chjj/marked
16635  */
16636
16637
16638 /**
16639  *
16640  * Roo.Markdown - is a very crude wrapper around marked..
16641  *
16642  * usage:
16643  * 
16644  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16645  * 
16646  * Note: move the sample code to the bottom of this
16647  * file before uncommenting it.
16648  *
16649  */
16650
16651 Roo.Markdown = {};
16652 Roo.Markdown.toHtml = function(text) {
16653     
16654     var c = new Roo.Markdown.marked.setOptions({
16655             renderer: new Roo.Markdown.marked.Renderer(),
16656             gfm: true,
16657             tables: true,
16658             breaks: false,
16659             pedantic: false,
16660             sanitize: false,
16661             smartLists: true,
16662             smartypants: false
16663           });
16664     // A FEW HACKS!!?
16665     
16666     text = text.replace(/\\\n/g,' ');
16667     return Roo.Markdown.marked(text);
16668 };
16669 //
16670 // converter
16671 //
16672 // Wraps all "globals" so that the only thing
16673 // exposed is makeHtml().
16674 //
16675 (function() {
16676     
16677     /**
16678      * Block-Level Grammar
16679      */
16680     
16681     var block = {
16682       newline: /^\n+/,
16683       code: /^( {4}[^\n]+\n*)+/,
16684       fences: noop,
16685       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16686       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16687       nptable: noop,
16688       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16689       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16690       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16691       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16692       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16693       table: noop,
16694       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16695       text: /^[^\n]+/
16696     };
16697     
16698     block.bullet = /(?:[*+-]|\d+\.)/;
16699     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16700     block.item = replace(block.item, 'gm')
16701       (/bull/g, block.bullet)
16702       ();
16703     
16704     block.list = replace(block.list)
16705       (/bull/g, block.bullet)
16706       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16707       ('def', '\\n+(?=' + block.def.source + ')')
16708       ();
16709     
16710     block.blockquote = replace(block.blockquote)
16711       ('def', block.def)
16712       ();
16713     
16714     block._tag = '(?!(?:'
16715       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16716       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16717       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16718     
16719     block.html = replace(block.html)
16720       ('comment', /<!--[\s\S]*?-->/)
16721       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16722       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16723       (/tag/g, block._tag)
16724       ();
16725     
16726     block.paragraph = replace(block.paragraph)
16727       ('hr', block.hr)
16728       ('heading', block.heading)
16729       ('lheading', block.lheading)
16730       ('blockquote', block.blockquote)
16731       ('tag', '<' + block._tag)
16732       ('def', block.def)
16733       ();
16734     
16735     /**
16736      * Normal Block Grammar
16737      */
16738     
16739     block.normal = merge({}, block);
16740     
16741     /**
16742      * GFM Block Grammar
16743      */
16744     
16745     block.gfm = merge({}, block.normal, {
16746       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16747       paragraph: /^/,
16748       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16749     });
16750     
16751     block.gfm.paragraph = replace(block.paragraph)
16752       ('(?!', '(?!'
16753         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16754         + block.list.source.replace('\\1', '\\3') + '|')
16755       ();
16756     
16757     /**
16758      * GFM + Tables Block Grammar
16759      */
16760     
16761     block.tables = merge({}, block.gfm, {
16762       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16763       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16764     });
16765     
16766     /**
16767      * Block Lexer
16768      */
16769     
16770     function Lexer(options) {
16771       this.tokens = [];
16772       this.tokens.links = {};
16773       this.options = options || marked.defaults;
16774       this.rules = block.normal;
16775     
16776       if (this.options.gfm) {
16777         if (this.options.tables) {
16778           this.rules = block.tables;
16779         } else {
16780           this.rules = block.gfm;
16781         }
16782       }
16783     }
16784     
16785     /**
16786      * Expose Block Rules
16787      */
16788     
16789     Lexer.rules = block;
16790     
16791     /**
16792      * Static Lex Method
16793      */
16794     
16795     Lexer.lex = function(src, options) {
16796       var lexer = new Lexer(options);
16797       return lexer.lex(src);
16798     };
16799     
16800     /**
16801      * Preprocessing
16802      */
16803     
16804     Lexer.prototype.lex = function(src) {
16805       src = src
16806         .replace(/\r\n|\r/g, '\n')
16807         .replace(/\t/g, '    ')
16808         .replace(/\u00a0/g, ' ')
16809         .replace(/\u2424/g, '\n');
16810     
16811       return this.token(src, true);
16812     };
16813     
16814     /**
16815      * Lexing
16816      */
16817     
16818     Lexer.prototype.token = function(src, top, bq) {
16819       var src = src.replace(/^ +$/gm, '')
16820         , next
16821         , loose
16822         , cap
16823         , bull
16824         , b
16825         , item
16826         , space
16827         , i
16828         , l;
16829     
16830       while (src) {
16831         // newline
16832         if (cap = this.rules.newline.exec(src)) {
16833           src = src.substring(cap[0].length);
16834           if (cap[0].length > 1) {
16835             this.tokens.push({
16836               type: 'space'
16837             });
16838           }
16839         }
16840     
16841         // code
16842         if (cap = this.rules.code.exec(src)) {
16843           src = src.substring(cap[0].length);
16844           cap = cap[0].replace(/^ {4}/gm, '');
16845           this.tokens.push({
16846             type: 'code',
16847             text: !this.options.pedantic
16848               ? cap.replace(/\n+$/, '')
16849               : cap
16850           });
16851           continue;
16852         }
16853     
16854         // fences (gfm)
16855         if (cap = this.rules.fences.exec(src)) {
16856           src = src.substring(cap[0].length);
16857           this.tokens.push({
16858             type: 'code',
16859             lang: cap[2],
16860             text: cap[3] || ''
16861           });
16862           continue;
16863         }
16864     
16865         // heading
16866         if (cap = this.rules.heading.exec(src)) {
16867           src = src.substring(cap[0].length);
16868           this.tokens.push({
16869             type: 'heading',
16870             depth: cap[1].length,
16871             text: cap[2]
16872           });
16873           continue;
16874         }
16875     
16876         // table no leading pipe (gfm)
16877         if (top && (cap = this.rules.nptable.exec(src))) {
16878           src = src.substring(cap[0].length);
16879     
16880           item = {
16881             type: 'table',
16882             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
16883             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
16884             cells: cap[3].replace(/\n$/, '').split('\n')
16885           };
16886     
16887           for (i = 0; i < item.align.length; i++) {
16888             if (/^ *-+: *$/.test(item.align[i])) {
16889               item.align[i] = 'right';
16890             } else if (/^ *:-+: *$/.test(item.align[i])) {
16891               item.align[i] = 'center';
16892             } else if (/^ *:-+ *$/.test(item.align[i])) {
16893               item.align[i] = 'left';
16894             } else {
16895               item.align[i] = null;
16896             }
16897           }
16898     
16899           for (i = 0; i < item.cells.length; i++) {
16900             item.cells[i] = item.cells[i].split(/ *\| */);
16901           }
16902     
16903           this.tokens.push(item);
16904     
16905           continue;
16906         }
16907     
16908         // lheading
16909         if (cap = this.rules.lheading.exec(src)) {
16910           src = src.substring(cap[0].length);
16911           this.tokens.push({
16912             type: 'heading',
16913             depth: cap[2] === '=' ? 1 : 2,
16914             text: cap[1]
16915           });
16916           continue;
16917         }
16918     
16919         // hr
16920         if (cap = this.rules.hr.exec(src)) {
16921           src = src.substring(cap[0].length);
16922           this.tokens.push({
16923             type: 'hr'
16924           });
16925           continue;
16926         }
16927     
16928         // blockquote
16929         if (cap = this.rules.blockquote.exec(src)) {
16930           src = src.substring(cap[0].length);
16931     
16932           this.tokens.push({
16933             type: 'blockquote_start'
16934           });
16935     
16936           cap = cap[0].replace(/^ *> ?/gm, '');
16937     
16938           // Pass `top` to keep the current
16939           // "toplevel" state. This is exactly
16940           // how markdown.pl works.
16941           this.token(cap, top, true);
16942     
16943           this.tokens.push({
16944             type: 'blockquote_end'
16945           });
16946     
16947           continue;
16948         }
16949     
16950         // list
16951         if (cap = this.rules.list.exec(src)) {
16952           src = src.substring(cap[0].length);
16953           bull = cap[2];
16954     
16955           this.tokens.push({
16956             type: 'list_start',
16957             ordered: bull.length > 1
16958           });
16959     
16960           // Get each top-level item.
16961           cap = cap[0].match(this.rules.item);
16962     
16963           next = false;
16964           l = cap.length;
16965           i = 0;
16966     
16967           for (; i < l; i++) {
16968             item = cap[i];
16969     
16970             // Remove the list item's bullet
16971             // so it is seen as the next token.
16972             space = item.length;
16973             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
16974     
16975             // Outdent whatever the
16976             // list item contains. Hacky.
16977             if (~item.indexOf('\n ')) {
16978               space -= item.length;
16979               item = !this.options.pedantic
16980                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
16981                 : item.replace(/^ {1,4}/gm, '');
16982             }
16983     
16984             // Determine whether the next list item belongs here.
16985             // Backpedal if it does not belong in this list.
16986             if (this.options.smartLists && i !== l - 1) {
16987               b = block.bullet.exec(cap[i + 1])[0];
16988               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
16989                 src = cap.slice(i + 1).join('\n') + src;
16990                 i = l - 1;
16991               }
16992             }
16993     
16994             // Determine whether item is loose or not.
16995             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
16996             // for discount behavior.
16997             loose = next || /\n\n(?!\s*$)/.test(item);
16998             if (i !== l - 1) {
16999               next = item.charAt(item.length - 1) === '\n';
17000               if (!loose) { loose = next; }
17001             }
17002     
17003             this.tokens.push({
17004               type: loose
17005                 ? 'loose_item_start'
17006                 : 'list_item_start'
17007             });
17008     
17009             // Recurse.
17010             this.token(item, false, bq);
17011     
17012             this.tokens.push({
17013               type: 'list_item_end'
17014             });
17015           }
17016     
17017           this.tokens.push({
17018             type: 'list_end'
17019           });
17020     
17021           continue;
17022         }
17023     
17024         // html
17025         if (cap = this.rules.html.exec(src)) {
17026           src = src.substring(cap[0].length);
17027           this.tokens.push({
17028             type: this.options.sanitize
17029               ? 'paragraph'
17030               : 'html',
17031             pre: !this.options.sanitizer
17032               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17033             text: cap[0]
17034           });
17035           continue;
17036         }
17037     
17038         // def
17039         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17040           src = src.substring(cap[0].length);
17041           this.tokens.links[cap[1].toLowerCase()] = {
17042             href: cap[2],
17043             title: cap[3]
17044           };
17045           continue;
17046         }
17047     
17048         // table (gfm)
17049         if (top && (cap = this.rules.table.exec(src))) {
17050           src = src.substring(cap[0].length);
17051     
17052           item = {
17053             type: 'table',
17054             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17055             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17056             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17057           };
17058     
17059           for (i = 0; i < item.align.length; i++) {
17060             if (/^ *-+: *$/.test(item.align[i])) {
17061               item.align[i] = 'right';
17062             } else if (/^ *:-+: *$/.test(item.align[i])) {
17063               item.align[i] = 'center';
17064             } else if (/^ *:-+ *$/.test(item.align[i])) {
17065               item.align[i] = 'left';
17066             } else {
17067               item.align[i] = null;
17068             }
17069           }
17070     
17071           for (i = 0; i < item.cells.length; i++) {
17072             item.cells[i] = item.cells[i]
17073               .replace(/^ *\| *| *\| *$/g, '')
17074               .split(/ *\| */);
17075           }
17076     
17077           this.tokens.push(item);
17078     
17079           continue;
17080         }
17081     
17082         // top-level paragraph
17083         if (top && (cap = this.rules.paragraph.exec(src))) {
17084           src = src.substring(cap[0].length);
17085           this.tokens.push({
17086             type: 'paragraph',
17087             text: cap[1].charAt(cap[1].length - 1) === '\n'
17088               ? cap[1].slice(0, -1)
17089               : cap[1]
17090           });
17091           continue;
17092         }
17093     
17094         // text
17095         if (cap = this.rules.text.exec(src)) {
17096           // Top-level should never reach here.
17097           src = src.substring(cap[0].length);
17098           this.tokens.push({
17099             type: 'text',
17100             text: cap[0]
17101           });
17102           continue;
17103         }
17104     
17105         if (src) {
17106           throw new
17107             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17108         }
17109       }
17110     
17111       return this.tokens;
17112     };
17113     
17114     /**
17115      * Inline-Level Grammar
17116      */
17117     
17118     var inline = {
17119       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17120       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17121       url: noop,
17122       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17123       link: /^!?\[(inside)\]\(href\)/,
17124       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17125       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17126       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17127       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17128       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17129       br: /^ {2,}\n(?!\s*$)/,
17130       del: noop,
17131       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17132     };
17133     
17134     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17135     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17136     
17137     inline.link = replace(inline.link)
17138       ('inside', inline._inside)
17139       ('href', inline._href)
17140       ();
17141     
17142     inline.reflink = replace(inline.reflink)
17143       ('inside', inline._inside)
17144       ();
17145     
17146     /**
17147      * Normal Inline Grammar
17148      */
17149     
17150     inline.normal = merge({}, inline);
17151     
17152     /**
17153      * Pedantic Inline Grammar
17154      */
17155     
17156     inline.pedantic = merge({}, inline.normal, {
17157       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17158       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17159     });
17160     
17161     /**
17162      * GFM Inline Grammar
17163      */
17164     
17165     inline.gfm = merge({}, inline.normal, {
17166       escape: replace(inline.escape)('])', '~|])')(),
17167       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17168       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17169       text: replace(inline.text)
17170         (']|', '~]|')
17171         ('|', '|https?://|')
17172         ()
17173     });
17174     
17175     /**
17176      * GFM + Line Breaks Inline Grammar
17177      */
17178     
17179     inline.breaks = merge({}, inline.gfm, {
17180       br: replace(inline.br)('{2,}', '*')(),
17181       text: replace(inline.gfm.text)('{2,}', '*')()
17182     });
17183     
17184     /**
17185      * Inline Lexer & Compiler
17186      */
17187     
17188     function InlineLexer(links, options) {
17189       this.options = options || marked.defaults;
17190       this.links = links;
17191       this.rules = inline.normal;
17192       this.renderer = this.options.renderer || new Renderer;
17193       this.renderer.options = this.options;
17194     
17195       if (!this.links) {
17196         throw new
17197           Error('Tokens array requires a `links` property.');
17198       }
17199     
17200       if (this.options.gfm) {
17201         if (this.options.breaks) {
17202           this.rules = inline.breaks;
17203         } else {
17204           this.rules = inline.gfm;
17205         }
17206       } else if (this.options.pedantic) {
17207         this.rules = inline.pedantic;
17208       }
17209     }
17210     
17211     /**
17212      * Expose Inline Rules
17213      */
17214     
17215     InlineLexer.rules = inline;
17216     
17217     /**
17218      * Static Lexing/Compiling Method
17219      */
17220     
17221     InlineLexer.output = function(src, links, options) {
17222       var inline = new InlineLexer(links, options);
17223       return inline.output(src);
17224     };
17225     
17226     /**
17227      * Lexing/Compiling
17228      */
17229     
17230     InlineLexer.prototype.output = function(src) {
17231       var out = ''
17232         , link
17233         , text
17234         , href
17235         , cap;
17236     
17237       while (src) {
17238         // escape
17239         if (cap = this.rules.escape.exec(src)) {
17240           src = src.substring(cap[0].length);
17241           out += cap[1];
17242           continue;
17243         }
17244     
17245         // autolink
17246         if (cap = this.rules.autolink.exec(src)) {
17247           src = src.substring(cap[0].length);
17248           if (cap[2] === '@') {
17249             text = cap[1].charAt(6) === ':'
17250               ? this.mangle(cap[1].substring(7))
17251               : this.mangle(cap[1]);
17252             href = this.mangle('mailto:') + text;
17253           } else {
17254             text = escape(cap[1]);
17255             href = text;
17256           }
17257           out += this.renderer.link(href, null, text);
17258           continue;
17259         }
17260     
17261         // url (gfm)
17262         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17263           src = src.substring(cap[0].length);
17264           text = escape(cap[1]);
17265           href = text;
17266           out += this.renderer.link(href, null, text);
17267           continue;
17268         }
17269     
17270         // tag
17271         if (cap = this.rules.tag.exec(src)) {
17272           if (!this.inLink && /^<a /i.test(cap[0])) {
17273             this.inLink = true;
17274           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17275             this.inLink = false;
17276           }
17277           src = src.substring(cap[0].length);
17278           out += this.options.sanitize
17279             ? this.options.sanitizer
17280               ? this.options.sanitizer(cap[0])
17281               : escape(cap[0])
17282             : cap[0];
17283           continue;
17284         }
17285     
17286         // link
17287         if (cap = this.rules.link.exec(src)) {
17288           src = src.substring(cap[0].length);
17289           this.inLink = true;
17290           out += this.outputLink(cap, {
17291             href: cap[2],
17292             title: cap[3]
17293           });
17294           this.inLink = false;
17295           continue;
17296         }
17297     
17298         // reflink, nolink
17299         if ((cap = this.rules.reflink.exec(src))
17300             || (cap = this.rules.nolink.exec(src))) {
17301           src = src.substring(cap[0].length);
17302           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17303           link = this.links[link.toLowerCase()];
17304           if (!link || !link.href) {
17305             out += cap[0].charAt(0);
17306             src = cap[0].substring(1) + src;
17307             continue;
17308           }
17309           this.inLink = true;
17310           out += this.outputLink(cap, link);
17311           this.inLink = false;
17312           continue;
17313         }
17314     
17315         // strong
17316         if (cap = this.rules.strong.exec(src)) {
17317           src = src.substring(cap[0].length);
17318           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17319           continue;
17320         }
17321     
17322         // em
17323         if (cap = this.rules.em.exec(src)) {
17324           src = src.substring(cap[0].length);
17325           out += this.renderer.em(this.output(cap[2] || cap[1]));
17326           continue;
17327         }
17328     
17329         // code
17330         if (cap = this.rules.code.exec(src)) {
17331           src = src.substring(cap[0].length);
17332           out += this.renderer.codespan(escape(cap[2], true));
17333           continue;
17334         }
17335     
17336         // br
17337         if (cap = this.rules.br.exec(src)) {
17338           src = src.substring(cap[0].length);
17339           out += this.renderer.br();
17340           continue;
17341         }
17342     
17343         // del (gfm)
17344         if (cap = this.rules.del.exec(src)) {
17345           src = src.substring(cap[0].length);
17346           out += this.renderer.del(this.output(cap[1]));
17347           continue;
17348         }
17349     
17350         // text
17351         if (cap = this.rules.text.exec(src)) {
17352           src = src.substring(cap[0].length);
17353           out += this.renderer.text(escape(this.smartypants(cap[0])));
17354           continue;
17355         }
17356     
17357         if (src) {
17358           throw new
17359             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17360         }
17361       }
17362     
17363       return out;
17364     };
17365     
17366     /**
17367      * Compile Link
17368      */
17369     
17370     InlineLexer.prototype.outputLink = function(cap, link) {
17371       var href = escape(link.href)
17372         , title = link.title ? escape(link.title) : null;
17373     
17374       return cap[0].charAt(0) !== '!'
17375         ? this.renderer.link(href, title, this.output(cap[1]))
17376         : this.renderer.image(href, title, escape(cap[1]));
17377     };
17378     
17379     /**
17380      * Smartypants Transformations
17381      */
17382     
17383     InlineLexer.prototype.smartypants = function(text) {
17384       if (!this.options.smartypants)  { return text; }
17385       return text
17386         // em-dashes
17387         .replace(/---/g, '\u2014')
17388         // en-dashes
17389         .replace(/--/g, '\u2013')
17390         // opening singles
17391         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17392         // closing singles & apostrophes
17393         .replace(/'/g, '\u2019')
17394         // opening doubles
17395         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17396         // closing doubles
17397         .replace(/"/g, '\u201d')
17398         // ellipses
17399         .replace(/\.{3}/g, '\u2026');
17400     };
17401     
17402     /**
17403      * Mangle Links
17404      */
17405     
17406     InlineLexer.prototype.mangle = function(text) {
17407       if (!this.options.mangle) { return text; }
17408       var out = ''
17409         , l = text.length
17410         , i = 0
17411         , ch;
17412     
17413       for (; i < l; i++) {
17414         ch = text.charCodeAt(i);
17415         if (Math.random() > 0.5) {
17416           ch = 'x' + ch.toString(16);
17417         }
17418         out += '&#' + ch + ';';
17419       }
17420     
17421       return out;
17422     };
17423     
17424     /**
17425      * Renderer
17426      */
17427     
17428     function Renderer(options) {
17429       this.options = options || {};
17430     }
17431     
17432     Renderer.prototype.code = function(code, lang, escaped) {
17433       if (this.options.highlight) {
17434         var out = this.options.highlight(code, lang);
17435         if (out != null && out !== code) {
17436           escaped = true;
17437           code = out;
17438         }
17439       } else {
17440             // hack!!! - it's already escapeD?
17441             escaped = true;
17442       }
17443     
17444       if (!lang) {
17445         return '<pre><code>'
17446           + (escaped ? code : escape(code, true))
17447           + '\n</code></pre>';
17448       }
17449     
17450       return '<pre><code class="'
17451         + this.options.langPrefix
17452         + escape(lang, true)
17453         + '">'
17454         + (escaped ? code : escape(code, true))
17455         + '\n</code></pre>\n';
17456     };
17457     
17458     Renderer.prototype.blockquote = function(quote) {
17459       return '<blockquote>\n' + quote + '</blockquote>\n';
17460     };
17461     
17462     Renderer.prototype.html = function(html) {
17463       return html;
17464     };
17465     
17466     Renderer.prototype.heading = function(text, level, raw) {
17467       return '<h'
17468         + level
17469         + ' id="'
17470         + this.options.headerPrefix
17471         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17472         + '">'
17473         + text
17474         + '</h'
17475         + level
17476         + '>\n';
17477     };
17478     
17479     Renderer.prototype.hr = function() {
17480       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17481     };
17482     
17483     Renderer.prototype.list = function(body, ordered) {
17484       var type = ordered ? 'ol' : 'ul';
17485       return '<' + type + '>\n' + body + '</' + type + '>\n';
17486     };
17487     
17488     Renderer.prototype.listitem = function(text) {
17489       return '<li>' + text + '</li>\n';
17490     };
17491     
17492     Renderer.prototype.paragraph = function(text) {
17493       return '<p>' + text + '</p>\n';
17494     };
17495     
17496     Renderer.prototype.table = function(header, body) {
17497       return '<table class="table table-striped">\n'
17498         + '<thead>\n'
17499         + header
17500         + '</thead>\n'
17501         + '<tbody>\n'
17502         + body
17503         + '</tbody>\n'
17504         + '</table>\n';
17505     };
17506     
17507     Renderer.prototype.tablerow = function(content) {
17508       return '<tr>\n' + content + '</tr>\n';
17509     };
17510     
17511     Renderer.prototype.tablecell = function(content, flags) {
17512       var type = flags.header ? 'th' : 'td';
17513       var tag = flags.align
17514         ? '<' + type + ' style="text-align:' + flags.align + '">'
17515         : '<' + type + '>';
17516       return tag + content + '</' + type + '>\n';
17517     };
17518     
17519     // span level renderer
17520     Renderer.prototype.strong = function(text) {
17521       return '<strong>' + text + '</strong>';
17522     };
17523     
17524     Renderer.prototype.em = function(text) {
17525       return '<em>' + text + '</em>';
17526     };
17527     
17528     Renderer.prototype.codespan = function(text) {
17529       return '<code>' + text + '</code>';
17530     };
17531     
17532     Renderer.prototype.br = function() {
17533       return this.options.xhtml ? '<br/>' : '<br>';
17534     };
17535     
17536     Renderer.prototype.del = function(text) {
17537       return '<del>' + text + '</del>';
17538     };
17539     
17540     Renderer.prototype.link = function(href, title, text) {
17541       if (this.options.sanitize) {
17542         try {
17543           var prot = decodeURIComponent(unescape(href))
17544             .replace(/[^\w:]/g, '')
17545             .toLowerCase();
17546         } catch (e) {
17547           return '';
17548         }
17549         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17550           return '';
17551         }
17552       }
17553       var out = '<a href="' + href + '"';
17554       if (title) {
17555         out += ' title="' + title + '"';
17556       }
17557       out += '>' + text + '</a>';
17558       return out;
17559     };
17560     
17561     Renderer.prototype.image = function(href, title, text) {
17562       var out = '<img src="' + href + '" alt="' + text + '"';
17563       if (title) {
17564         out += ' title="' + title + '"';
17565       }
17566       out += this.options.xhtml ? '/>' : '>';
17567       return out;
17568     };
17569     
17570     Renderer.prototype.text = function(text) {
17571       return text;
17572     };
17573     
17574     /**
17575      * Parsing & Compiling
17576      */
17577     
17578     function Parser(options) {
17579       this.tokens = [];
17580       this.token = null;
17581       this.options = options || marked.defaults;
17582       this.options.renderer = this.options.renderer || new Renderer;
17583       this.renderer = this.options.renderer;
17584       this.renderer.options = this.options;
17585     }
17586     
17587     /**
17588      * Static Parse Method
17589      */
17590     
17591     Parser.parse = function(src, options, renderer) {
17592       var parser = new Parser(options, renderer);
17593       return parser.parse(src);
17594     };
17595     
17596     /**
17597      * Parse Loop
17598      */
17599     
17600     Parser.prototype.parse = function(src) {
17601       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17602       this.tokens = src.reverse();
17603     
17604       var out = '';
17605       while (this.next()) {
17606         out += this.tok();
17607       }
17608     
17609       return out;
17610     };
17611     
17612     /**
17613      * Next Token
17614      */
17615     
17616     Parser.prototype.next = function() {
17617       return this.token = this.tokens.pop();
17618     };
17619     
17620     /**
17621      * Preview Next Token
17622      */
17623     
17624     Parser.prototype.peek = function() {
17625       return this.tokens[this.tokens.length - 1] || 0;
17626     };
17627     
17628     /**
17629      * Parse Text Tokens
17630      */
17631     
17632     Parser.prototype.parseText = function() {
17633       var body = this.token.text;
17634     
17635       while (this.peek().type === 'text') {
17636         body += '\n' + this.next().text;
17637       }
17638     
17639       return this.inline.output(body);
17640     };
17641     
17642     /**
17643      * Parse Current Token
17644      */
17645     
17646     Parser.prototype.tok = function() {
17647       switch (this.token.type) {
17648         case 'space': {
17649           return '';
17650         }
17651         case 'hr': {
17652           return this.renderer.hr();
17653         }
17654         case 'heading': {
17655           return this.renderer.heading(
17656             this.inline.output(this.token.text),
17657             this.token.depth,
17658             this.token.text);
17659         }
17660         case 'code': {
17661           return this.renderer.code(this.token.text,
17662             this.token.lang,
17663             this.token.escaped);
17664         }
17665         case 'table': {
17666           var header = ''
17667             , body = ''
17668             , i
17669             , row
17670             , cell
17671             , flags
17672             , j;
17673     
17674           // header
17675           cell = '';
17676           for (i = 0; i < this.token.header.length; i++) {
17677             flags = { header: true, align: this.token.align[i] };
17678             cell += this.renderer.tablecell(
17679               this.inline.output(this.token.header[i]),
17680               { header: true, align: this.token.align[i] }
17681             );
17682           }
17683           header += this.renderer.tablerow(cell);
17684     
17685           for (i = 0; i < this.token.cells.length; i++) {
17686             row = this.token.cells[i];
17687     
17688             cell = '';
17689             for (j = 0; j < row.length; j++) {
17690               cell += this.renderer.tablecell(
17691                 this.inline.output(row[j]),
17692                 { header: false, align: this.token.align[j] }
17693               );
17694             }
17695     
17696             body += this.renderer.tablerow(cell);
17697           }
17698           return this.renderer.table(header, body);
17699         }
17700         case 'blockquote_start': {
17701           var body = '';
17702     
17703           while (this.next().type !== 'blockquote_end') {
17704             body += this.tok();
17705           }
17706     
17707           return this.renderer.blockquote(body);
17708         }
17709         case 'list_start': {
17710           var body = ''
17711             , ordered = this.token.ordered;
17712     
17713           while (this.next().type !== 'list_end') {
17714             body += this.tok();
17715           }
17716     
17717           return this.renderer.list(body, ordered);
17718         }
17719         case 'list_item_start': {
17720           var body = '';
17721     
17722           while (this.next().type !== 'list_item_end') {
17723             body += this.token.type === 'text'
17724               ? this.parseText()
17725               : this.tok();
17726           }
17727     
17728           return this.renderer.listitem(body);
17729         }
17730         case 'loose_item_start': {
17731           var body = '';
17732     
17733           while (this.next().type !== 'list_item_end') {
17734             body += this.tok();
17735           }
17736     
17737           return this.renderer.listitem(body);
17738         }
17739         case 'html': {
17740           var html = !this.token.pre && !this.options.pedantic
17741             ? this.inline.output(this.token.text)
17742             : this.token.text;
17743           return this.renderer.html(html);
17744         }
17745         case 'paragraph': {
17746           return this.renderer.paragraph(this.inline.output(this.token.text));
17747         }
17748         case 'text': {
17749           return this.renderer.paragraph(this.parseText());
17750         }
17751       }
17752     };
17753     
17754     /**
17755      * Helpers
17756      */
17757     
17758     function escape(html, encode) {
17759       return html
17760         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17761         .replace(/</g, '&lt;')
17762         .replace(/>/g, '&gt;')
17763         .replace(/"/g, '&quot;')
17764         .replace(/'/g, '&#39;');
17765     }
17766     
17767     function unescape(html) {
17768         // explicitly match decimal, hex, and named HTML entities 
17769       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17770         n = n.toLowerCase();
17771         if (n === 'colon') { return ':'; }
17772         if (n.charAt(0) === '#') {
17773           return n.charAt(1) === 'x'
17774             ? String.fromCharCode(parseInt(n.substring(2), 16))
17775             : String.fromCharCode(+n.substring(1));
17776         }
17777         return '';
17778       });
17779     }
17780     
17781     function replace(regex, opt) {
17782       regex = regex.source;
17783       opt = opt || '';
17784       return function self(name, val) {
17785         if (!name) { return new RegExp(regex, opt); }
17786         val = val.source || val;
17787         val = val.replace(/(^|[^\[])\^/g, '$1');
17788         regex = regex.replace(name, val);
17789         return self;
17790       };
17791     }
17792     
17793     function noop() {}
17794     noop.exec = noop;
17795     
17796     function merge(obj) {
17797       var i = 1
17798         , target
17799         , key;
17800     
17801       for (; i < arguments.length; i++) {
17802         target = arguments[i];
17803         for (key in target) {
17804           if (Object.prototype.hasOwnProperty.call(target, key)) {
17805             obj[key] = target[key];
17806           }
17807         }
17808       }
17809     
17810       return obj;
17811     }
17812     
17813     
17814     /**
17815      * Marked
17816      */
17817     
17818     function marked(src, opt, callback) {
17819       if (callback || typeof opt === 'function') {
17820         if (!callback) {
17821           callback = opt;
17822           opt = null;
17823         }
17824     
17825         opt = merge({}, marked.defaults, opt || {});
17826     
17827         var highlight = opt.highlight
17828           , tokens
17829           , pending
17830           , i = 0;
17831     
17832         try {
17833           tokens = Lexer.lex(src, opt)
17834         } catch (e) {
17835           return callback(e);
17836         }
17837     
17838         pending = tokens.length;
17839     
17840         var done = function(err) {
17841           if (err) {
17842             opt.highlight = highlight;
17843             return callback(err);
17844           }
17845     
17846           var out;
17847     
17848           try {
17849             out = Parser.parse(tokens, opt);
17850           } catch (e) {
17851             err = e;
17852           }
17853     
17854           opt.highlight = highlight;
17855     
17856           return err
17857             ? callback(err)
17858             : callback(null, out);
17859         };
17860     
17861         if (!highlight || highlight.length < 3) {
17862           return done();
17863         }
17864     
17865         delete opt.highlight;
17866     
17867         if (!pending) { return done(); }
17868     
17869         for (; i < tokens.length; i++) {
17870           (function(token) {
17871             if (token.type !== 'code') {
17872               return --pending || done();
17873             }
17874             return highlight(token.text, token.lang, function(err, code) {
17875               if (err) { return done(err); }
17876               if (code == null || code === token.text) {
17877                 return --pending || done();
17878               }
17879               token.text = code;
17880               token.escaped = true;
17881               --pending || done();
17882             });
17883           })(tokens[i]);
17884         }
17885     
17886         return;
17887       }
17888       try {
17889         if (opt) { opt = merge({}, marked.defaults, opt); }
17890         return Parser.parse(Lexer.lex(src, opt), opt);
17891       } catch (e) {
17892         e.message += '\nPlease report this to https://github.com/chjj/marked.';
17893         if ((opt || marked.defaults).silent) {
17894           return '<p>An error occured:</p><pre>'
17895             + escape(e.message + '', true)
17896             + '</pre>';
17897         }
17898         throw e;
17899       }
17900     }
17901     
17902     /**
17903      * Options
17904      */
17905     
17906     marked.options =
17907     marked.setOptions = function(opt) {
17908       merge(marked.defaults, opt);
17909       return marked;
17910     };
17911     
17912     marked.defaults = {
17913       gfm: true,
17914       tables: true,
17915       breaks: false,
17916       pedantic: false,
17917       sanitize: false,
17918       sanitizer: null,
17919       mangle: true,
17920       smartLists: false,
17921       silent: false,
17922       highlight: null,
17923       langPrefix: 'lang-',
17924       smartypants: false,
17925       headerPrefix: '',
17926       renderer: new Renderer,
17927       xhtml: false
17928     };
17929     
17930     /**
17931      * Expose
17932      */
17933     
17934     marked.Parser = Parser;
17935     marked.parser = Parser.parse;
17936     
17937     marked.Renderer = Renderer;
17938     
17939     marked.Lexer = Lexer;
17940     marked.lexer = Lexer.lex;
17941     
17942     marked.InlineLexer = InlineLexer;
17943     marked.inlineLexer = InlineLexer.output;
17944     
17945     marked.parse = marked;
17946     
17947     Roo.Markdown.marked = marked;
17948
17949 })();/*
17950  * Based on:
17951  * Ext JS Library 1.1.1
17952  * Copyright(c) 2006-2007, Ext JS, LLC.
17953  *
17954  * Originally Released Under LGPL - original licence link has changed is not relivant.
17955  *
17956  * Fork - LGPL
17957  * <script type="text/javascript">
17958  */
17959
17960
17961
17962 /*
17963  * These classes are derivatives of the similarly named classes in the YUI Library.
17964  * The original license:
17965  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
17966  * Code licensed under the BSD License:
17967  * http://developer.yahoo.net/yui/license.txt
17968  */
17969
17970 (function() {
17971
17972 var Event=Roo.EventManager;
17973 var Dom=Roo.lib.Dom;
17974
17975 /**
17976  * @class Roo.dd.DragDrop
17977  * @extends Roo.util.Observable
17978  * Defines the interface and base operation of items that that can be
17979  * dragged or can be drop targets.  It was designed to be extended, overriding
17980  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
17981  * Up to three html elements can be associated with a DragDrop instance:
17982  * <ul>
17983  * <li>linked element: the element that is passed into the constructor.
17984  * This is the element which defines the boundaries for interaction with
17985  * other DragDrop objects.</li>
17986  * <li>handle element(s): The drag operation only occurs if the element that
17987  * was clicked matches a handle element.  By default this is the linked
17988  * element, but there are times that you will want only a portion of the
17989  * linked element to initiate the drag operation, and the setHandleElId()
17990  * method provides a way to define this.</li>
17991  * <li>drag element: this represents the element that would be moved along
17992  * with the cursor during a drag operation.  By default, this is the linked
17993  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
17994  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
17995  * </li>
17996  * </ul>
17997  * This class should not be instantiated until the onload event to ensure that
17998  * the associated elements are available.
17999  * The following would define a DragDrop obj that would interact with any
18000  * other DragDrop obj in the "group1" group:
18001  * <pre>
18002  *  dd = new Roo.dd.DragDrop("div1", "group1");
18003  * </pre>
18004  * Since none of the event handlers have been implemented, nothing would
18005  * actually happen if you were to run the code above.  Normally you would
18006  * override this class or one of the default implementations, but you can
18007  * also override the methods you want on an instance of the class...
18008  * <pre>
18009  *  dd.onDragDrop = function(e, id) {
18010  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18011  *  }
18012  * </pre>
18013  * @constructor
18014  * @param {String} id of the element that is linked to this instance
18015  * @param {String} sGroup the group of related DragDrop objects
18016  * @param {object} config an object containing configurable attributes
18017  *                Valid properties for DragDrop:
18018  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18019  */
18020 Roo.dd.DragDrop = function(id, sGroup, config) {
18021     if (id) {
18022         this.init(id, sGroup, config);
18023     }
18024     
18025 };
18026
18027 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18028
18029     /**
18030      * The id of the element associated with this object.  This is what we
18031      * refer to as the "linked element" because the size and position of
18032      * this element is used to determine when the drag and drop objects have
18033      * interacted.
18034      * @property id
18035      * @type String
18036      */
18037     id: null,
18038
18039     /**
18040      * Configuration attributes passed into the constructor
18041      * @property config
18042      * @type object
18043      */
18044     config: null,
18045
18046     /**
18047      * The id of the element that will be dragged.  By default this is same
18048      * as the linked element , but could be changed to another element. Ex:
18049      * Roo.dd.DDProxy
18050      * @property dragElId
18051      * @type String
18052      * @private
18053      */
18054     dragElId: null,
18055
18056     /**
18057      * the id of the element that initiates the drag operation.  By default
18058      * this is the linked element, but could be changed to be a child of this
18059      * element.  This lets us do things like only starting the drag when the
18060      * header element within the linked html element is clicked.
18061      * @property handleElId
18062      * @type String
18063      * @private
18064      */
18065     handleElId: null,
18066
18067     /**
18068      * An associative array of HTML tags that will be ignored if clicked.
18069      * @property invalidHandleTypes
18070      * @type {string: string}
18071      */
18072     invalidHandleTypes: null,
18073
18074     /**
18075      * An associative array of ids for elements that will be ignored if clicked
18076      * @property invalidHandleIds
18077      * @type {string: string}
18078      */
18079     invalidHandleIds: null,
18080
18081     /**
18082      * An indexted array of css class names for elements that will be ignored
18083      * if clicked.
18084      * @property invalidHandleClasses
18085      * @type string[]
18086      */
18087     invalidHandleClasses: null,
18088
18089     /**
18090      * The linked element's absolute X position at the time the drag was
18091      * started
18092      * @property startPageX
18093      * @type int
18094      * @private
18095      */
18096     startPageX: 0,
18097
18098     /**
18099      * The linked element's absolute X position at the time the drag was
18100      * started
18101      * @property startPageY
18102      * @type int
18103      * @private
18104      */
18105     startPageY: 0,
18106
18107     /**
18108      * The group defines a logical collection of DragDrop objects that are
18109      * related.  Instances only get events when interacting with other
18110      * DragDrop object in the same group.  This lets us define multiple
18111      * groups using a single DragDrop subclass if we want.
18112      * @property groups
18113      * @type {string: string}
18114      */
18115     groups: null,
18116
18117     /**
18118      * Individual drag/drop instances can be locked.  This will prevent
18119      * onmousedown start drag.
18120      * @property locked
18121      * @type boolean
18122      * @private
18123      */
18124     locked: false,
18125
18126     /**
18127      * Lock this instance
18128      * @method lock
18129      */
18130     lock: function() { this.locked = true; },
18131
18132     /**
18133      * Unlock this instace
18134      * @method unlock
18135      */
18136     unlock: function() { this.locked = false; },
18137
18138     /**
18139      * By default, all insances can be a drop target.  This can be disabled by
18140      * setting isTarget to false.
18141      * @method isTarget
18142      * @type boolean
18143      */
18144     isTarget: true,
18145
18146     /**
18147      * The padding configured for this drag and drop object for calculating
18148      * the drop zone intersection with this object.
18149      * @method padding
18150      * @type int[]
18151      */
18152     padding: null,
18153
18154     /**
18155      * Cached reference to the linked element
18156      * @property _domRef
18157      * @private
18158      */
18159     _domRef: null,
18160
18161     /**
18162      * Internal typeof flag
18163      * @property __ygDragDrop
18164      * @private
18165      */
18166     __ygDragDrop: true,
18167
18168     /**
18169      * Set to true when horizontal contraints are applied
18170      * @property constrainX
18171      * @type boolean
18172      * @private
18173      */
18174     constrainX: false,
18175
18176     /**
18177      * Set to true when vertical contraints are applied
18178      * @property constrainY
18179      * @type boolean
18180      * @private
18181      */
18182     constrainY: false,
18183
18184     /**
18185      * The left constraint
18186      * @property minX
18187      * @type int
18188      * @private
18189      */
18190     minX: 0,
18191
18192     /**
18193      * The right constraint
18194      * @property maxX
18195      * @type int
18196      * @private
18197      */
18198     maxX: 0,
18199
18200     /**
18201      * The up constraint
18202      * @property minY
18203      * @type int
18204      * @type int
18205      * @private
18206      */
18207     minY: 0,
18208
18209     /**
18210      * The down constraint
18211      * @property maxY
18212      * @type int
18213      * @private
18214      */
18215     maxY: 0,
18216
18217     /**
18218      * Maintain offsets when we resetconstraints.  Set to true when you want
18219      * the position of the element relative to its parent to stay the same
18220      * when the page changes
18221      *
18222      * @property maintainOffset
18223      * @type boolean
18224      */
18225     maintainOffset: false,
18226
18227     /**
18228      * Array of pixel locations the element will snap to if we specified a
18229      * horizontal graduation/interval.  This array is generated automatically
18230      * when you define a tick interval.
18231      * @property xTicks
18232      * @type int[]
18233      */
18234     xTicks: null,
18235
18236     /**
18237      * Array of pixel locations the element will snap to if we specified a
18238      * vertical graduation/interval.  This array is generated automatically
18239      * when you define a tick interval.
18240      * @property yTicks
18241      * @type int[]
18242      */
18243     yTicks: null,
18244
18245     /**
18246      * By default the drag and drop instance will only respond to the primary
18247      * button click (left button for a right-handed mouse).  Set to true to
18248      * allow drag and drop to start with any mouse click that is propogated
18249      * by the browser
18250      * @property primaryButtonOnly
18251      * @type boolean
18252      */
18253     primaryButtonOnly: true,
18254
18255     /**
18256      * The availabe property is false until the linked dom element is accessible.
18257      * @property available
18258      * @type boolean
18259      */
18260     available: false,
18261
18262     /**
18263      * By default, drags can only be initiated if the mousedown occurs in the
18264      * region the linked element is.  This is done in part to work around a
18265      * bug in some browsers that mis-report the mousedown if the previous
18266      * mouseup happened outside of the window.  This property is set to true
18267      * if outer handles are defined.
18268      *
18269      * @property hasOuterHandles
18270      * @type boolean
18271      * @default false
18272      */
18273     hasOuterHandles: false,
18274
18275     /**
18276      * Code that executes immediately before the startDrag event
18277      * @method b4StartDrag
18278      * @private
18279      */
18280     b4StartDrag: function(x, y) { },
18281
18282     /**
18283      * Abstract method called after a drag/drop object is clicked
18284      * and the drag or mousedown time thresholds have beeen met.
18285      * @method startDrag
18286      * @param {int} X click location
18287      * @param {int} Y click location
18288      */
18289     startDrag: function(x, y) { /* override this */ },
18290
18291     /**
18292      * Code that executes immediately before the onDrag event
18293      * @method b4Drag
18294      * @private
18295      */
18296     b4Drag: function(e) { },
18297
18298     /**
18299      * Abstract method called during the onMouseMove event while dragging an
18300      * object.
18301      * @method onDrag
18302      * @param {Event} e the mousemove event
18303      */
18304     onDrag: function(e) { /* override this */ },
18305
18306     /**
18307      * Abstract method called when this element fist begins hovering over
18308      * another DragDrop obj
18309      * @method onDragEnter
18310      * @param {Event} e the mousemove event
18311      * @param {String|DragDrop[]} id In POINT mode, the element
18312      * id this is hovering over.  In INTERSECT mode, an array of one or more
18313      * dragdrop items being hovered over.
18314      */
18315     onDragEnter: function(e, id) { /* override this */ },
18316
18317     /**
18318      * Code that executes immediately before the onDragOver event
18319      * @method b4DragOver
18320      * @private
18321      */
18322     b4DragOver: function(e) { },
18323
18324     /**
18325      * Abstract method called when this element is hovering over another
18326      * DragDrop obj
18327      * @method onDragOver
18328      * @param {Event} e the mousemove event
18329      * @param {String|DragDrop[]} id In POINT mode, the element
18330      * id this is hovering over.  In INTERSECT mode, an array of dd items
18331      * being hovered over.
18332      */
18333     onDragOver: function(e, id) { /* override this */ },
18334
18335     /**
18336      * Code that executes immediately before the onDragOut event
18337      * @method b4DragOut
18338      * @private
18339      */
18340     b4DragOut: function(e) { },
18341
18342     /**
18343      * Abstract method called when we are no longer hovering over an element
18344      * @method onDragOut
18345      * @param {Event} e the mousemove event
18346      * @param {String|DragDrop[]} id In POINT mode, the element
18347      * id this was hovering over.  In INTERSECT mode, an array of dd items
18348      * that the mouse is no longer over.
18349      */
18350     onDragOut: function(e, id) { /* override this */ },
18351
18352     /**
18353      * Code that executes immediately before the onDragDrop event
18354      * @method b4DragDrop
18355      * @private
18356      */
18357     b4DragDrop: function(e) { },
18358
18359     /**
18360      * Abstract method called when this item is dropped on another DragDrop
18361      * obj
18362      * @method onDragDrop
18363      * @param {Event} e the mouseup event
18364      * @param {String|DragDrop[]} id In POINT mode, the element
18365      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18366      * was dropped on.
18367      */
18368     onDragDrop: function(e, id) { /* override this */ },
18369
18370     /**
18371      * Abstract method called when this item is dropped on an area with no
18372      * drop target
18373      * @method onInvalidDrop
18374      * @param {Event} e the mouseup event
18375      */
18376     onInvalidDrop: function(e) { /* override this */ },
18377
18378     /**
18379      * Code that executes immediately before the endDrag event
18380      * @method b4EndDrag
18381      * @private
18382      */
18383     b4EndDrag: function(e) { },
18384
18385     /**
18386      * Fired when we are done dragging the object
18387      * @method endDrag
18388      * @param {Event} e the mouseup event
18389      */
18390     endDrag: function(e) { /* override this */ },
18391
18392     /**
18393      * Code executed immediately before the onMouseDown event
18394      * @method b4MouseDown
18395      * @param {Event} e the mousedown event
18396      * @private
18397      */
18398     b4MouseDown: function(e) {  },
18399
18400     /**
18401      * Event handler that fires when a drag/drop obj gets a mousedown
18402      * @method onMouseDown
18403      * @param {Event} e the mousedown event
18404      */
18405     onMouseDown: function(e) { /* override this */ },
18406
18407     /**
18408      * Event handler that fires when a drag/drop obj gets a mouseup
18409      * @method onMouseUp
18410      * @param {Event} e the mouseup event
18411      */
18412     onMouseUp: function(e) { /* override this */ },
18413
18414     /**
18415      * Override the onAvailable method to do what is needed after the initial
18416      * position was determined.
18417      * @method onAvailable
18418      */
18419     onAvailable: function () {
18420     },
18421
18422     /*
18423      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18424      * @type Object
18425      */
18426     defaultPadding : {left:0, right:0, top:0, bottom:0},
18427
18428     /*
18429      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18430  *
18431  * Usage:
18432  <pre><code>
18433  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18434                 { dragElId: "existingProxyDiv" });
18435  dd.startDrag = function(){
18436      this.constrainTo("parent-id");
18437  };
18438  </code></pre>
18439  * Or you can initalize it using the {@link Roo.Element} object:
18440  <pre><code>
18441  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18442      startDrag : function(){
18443          this.constrainTo("parent-id");
18444      }
18445  });
18446  </code></pre>
18447      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18448      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18449      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18450      * an object containing the sides to pad. For example: {right:10, bottom:10}
18451      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18452      */
18453     constrainTo : function(constrainTo, pad, inContent){
18454         if(typeof pad == "number"){
18455             pad = {left: pad, right:pad, top:pad, bottom:pad};
18456         }
18457         pad = pad || this.defaultPadding;
18458         var b = Roo.get(this.getEl()).getBox();
18459         var ce = Roo.get(constrainTo);
18460         var s = ce.getScroll();
18461         var c, cd = ce.dom;
18462         if(cd == document.body){
18463             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18464         }else{
18465             xy = ce.getXY();
18466             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18467         }
18468
18469
18470         var topSpace = b.y - c.y;
18471         var leftSpace = b.x - c.x;
18472
18473         this.resetConstraints();
18474         this.setXConstraint(leftSpace - (pad.left||0), // left
18475                 c.width - leftSpace - b.width - (pad.right||0) //right
18476         );
18477         this.setYConstraint(topSpace - (pad.top||0), //top
18478                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18479         );
18480     },
18481
18482     /**
18483      * Returns a reference to the linked element
18484      * @method getEl
18485      * @return {HTMLElement} the html element
18486      */
18487     getEl: function() {
18488         if (!this._domRef) {
18489             this._domRef = Roo.getDom(this.id);
18490         }
18491
18492         return this._domRef;
18493     },
18494
18495     /**
18496      * Returns a reference to the actual element to drag.  By default this is
18497      * the same as the html element, but it can be assigned to another
18498      * element. An example of this can be found in Roo.dd.DDProxy
18499      * @method getDragEl
18500      * @return {HTMLElement} the html element
18501      */
18502     getDragEl: function() {
18503         return Roo.getDom(this.dragElId);
18504     },
18505
18506     /**
18507      * Sets up the DragDrop object.  Must be called in the constructor of any
18508      * Roo.dd.DragDrop subclass
18509      * @method init
18510      * @param id the id of the linked element
18511      * @param {String} sGroup the group of related items
18512      * @param {object} config configuration attributes
18513      */
18514     init: function(id, sGroup, config) {
18515         this.initTarget(id, sGroup, config);
18516         if (!Roo.isTouch) {
18517             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18518         }
18519         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18520         // Event.on(this.id, "selectstart", Event.preventDefault);
18521     },
18522
18523     /**
18524      * Initializes Targeting functionality only... the object does not
18525      * get a mousedown handler.
18526      * @method initTarget
18527      * @param id the id of the linked element
18528      * @param {String} sGroup the group of related items
18529      * @param {object} config configuration attributes
18530      */
18531     initTarget: function(id, sGroup, config) {
18532
18533         // configuration attributes
18534         this.config = config || {};
18535
18536         // create a local reference to the drag and drop manager
18537         this.DDM = Roo.dd.DDM;
18538         // initialize the groups array
18539         this.groups = {};
18540
18541         // assume that we have an element reference instead of an id if the
18542         // parameter is not a string
18543         if (typeof id !== "string") {
18544             id = Roo.id(id);
18545         }
18546
18547         // set the id
18548         this.id = id;
18549
18550         // add to an interaction group
18551         this.addToGroup((sGroup) ? sGroup : "default");
18552
18553         // We don't want to register this as the handle with the manager
18554         // so we just set the id rather than calling the setter.
18555         this.handleElId = id;
18556
18557         // the linked element is the element that gets dragged by default
18558         this.setDragElId(id);
18559
18560         // by default, clicked anchors will not start drag operations.
18561         this.invalidHandleTypes = { A: "A" };
18562         this.invalidHandleIds = {};
18563         this.invalidHandleClasses = [];
18564
18565         this.applyConfig();
18566
18567         this.handleOnAvailable();
18568     },
18569
18570     /**
18571      * Applies the configuration parameters that were passed into the constructor.
18572      * This is supposed to happen at each level through the inheritance chain.  So
18573      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18574      * DragDrop in order to get all of the parameters that are available in
18575      * each object.
18576      * @method applyConfig
18577      */
18578     applyConfig: function() {
18579
18580         // configurable properties:
18581         //    padding, isTarget, maintainOffset, primaryButtonOnly
18582         this.padding           = this.config.padding || [0, 0, 0, 0];
18583         this.isTarget          = (this.config.isTarget !== false);
18584         this.maintainOffset    = (this.config.maintainOffset);
18585         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18586
18587     },
18588
18589     /**
18590      * Executed when the linked element is available
18591      * @method handleOnAvailable
18592      * @private
18593      */
18594     handleOnAvailable: function() {
18595         this.available = true;
18596         this.resetConstraints();
18597         this.onAvailable();
18598     },
18599
18600      /**
18601      * Configures the padding for the target zone in px.  Effectively expands
18602      * (or reduces) the virtual object size for targeting calculations.
18603      * Supports css-style shorthand; if only one parameter is passed, all sides
18604      * will have that padding, and if only two are passed, the top and bottom
18605      * will have the first param, the left and right the second.
18606      * @method setPadding
18607      * @param {int} iTop    Top pad
18608      * @param {int} iRight  Right pad
18609      * @param {int} iBot    Bot pad
18610      * @param {int} iLeft   Left pad
18611      */
18612     setPadding: function(iTop, iRight, iBot, iLeft) {
18613         // this.padding = [iLeft, iRight, iTop, iBot];
18614         if (!iRight && 0 !== iRight) {
18615             this.padding = [iTop, iTop, iTop, iTop];
18616         } else if (!iBot && 0 !== iBot) {
18617             this.padding = [iTop, iRight, iTop, iRight];
18618         } else {
18619             this.padding = [iTop, iRight, iBot, iLeft];
18620         }
18621     },
18622
18623     /**
18624      * Stores the initial placement of the linked element.
18625      * @method setInitialPosition
18626      * @param {int} diffX   the X offset, default 0
18627      * @param {int} diffY   the Y offset, default 0
18628      */
18629     setInitPosition: function(diffX, diffY) {
18630         var el = this.getEl();
18631
18632         if (!this.DDM.verifyEl(el)) {
18633             return;
18634         }
18635
18636         var dx = diffX || 0;
18637         var dy = diffY || 0;
18638
18639         var p = Dom.getXY( el );
18640
18641         this.initPageX = p[0] - dx;
18642         this.initPageY = p[1] - dy;
18643
18644         this.lastPageX = p[0];
18645         this.lastPageY = p[1];
18646
18647
18648         this.setStartPosition(p);
18649     },
18650
18651     /**
18652      * Sets the start position of the element.  This is set when the obj
18653      * is initialized, the reset when a drag is started.
18654      * @method setStartPosition
18655      * @param pos current position (from previous lookup)
18656      * @private
18657      */
18658     setStartPosition: function(pos) {
18659         var p = pos || Dom.getXY( this.getEl() );
18660         this.deltaSetXY = null;
18661
18662         this.startPageX = p[0];
18663         this.startPageY = p[1];
18664     },
18665
18666     /**
18667      * Add this instance to a group of related drag/drop objects.  All
18668      * instances belong to at least one group, and can belong to as many
18669      * groups as needed.
18670      * @method addToGroup
18671      * @param sGroup {string} the name of the group
18672      */
18673     addToGroup: function(sGroup) {
18674         this.groups[sGroup] = true;
18675         this.DDM.regDragDrop(this, sGroup);
18676     },
18677
18678     /**
18679      * Remove's this instance from the supplied interaction group
18680      * @method removeFromGroup
18681      * @param {string}  sGroup  The group to drop
18682      */
18683     removeFromGroup: function(sGroup) {
18684         if (this.groups[sGroup]) {
18685             delete this.groups[sGroup];
18686         }
18687
18688         this.DDM.removeDDFromGroup(this, sGroup);
18689     },
18690
18691     /**
18692      * Allows you to specify that an element other than the linked element
18693      * will be moved with the cursor during a drag
18694      * @method setDragElId
18695      * @param id {string} the id of the element that will be used to initiate the drag
18696      */
18697     setDragElId: function(id) {
18698         this.dragElId = id;
18699     },
18700
18701     /**
18702      * Allows you to specify a child of the linked element that should be
18703      * used to initiate the drag operation.  An example of this would be if
18704      * you have a content div with text and links.  Clicking anywhere in the
18705      * content area would normally start the drag operation.  Use this method
18706      * to specify that an element inside of the content div is the element
18707      * that starts the drag operation.
18708      * @method setHandleElId
18709      * @param id {string} the id of the element that will be used to
18710      * initiate the drag.
18711      */
18712     setHandleElId: function(id) {
18713         if (typeof id !== "string") {
18714             id = Roo.id(id);
18715         }
18716         this.handleElId = id;
18717         this.DDM.regHandle(this.id, id);
18718     },
18719
18720     /**
18721      * Allows you to set an element outside of the linked element as a drag
18722      * handle
18723      * @method setOuterHandleElId
18724      * @param id the id of the element that will be used to initiate the drag
18725      */
18726     setOuterHandleElId: function(id) {
18727         if (typeof id !== "string") {
18728             id = Roo.id(id);
18729         }
18730         Event.on(id, "mousedown",
18731                 this.handleMouseDown, this);
18732         this.setHandleElId(id);
18733
18734         this.hasOuterHandles = true;
18735     },
18736
18737     /**
18738      * Remove all drag and drop hooks for this element
18739      * @method unreg
18740      */
18741     unreg: function() {
18742         Event.un(this.id, "mousedown",
18743                 this.handleMouseDown);
18744         Event.un(this.id, "touchstart",
18745                 this.handleMouseDown);
18746         this._domRef = null;
18747         this.DDM._remove(this);
18748     },
18749
18750     destroy : function(){
18751         this.unreg();
18752     },
18753
18754     /**
18755      * Returns true if this instance is locked, or the drag drop mgr is locked
18756      * (meaning that all drag/drop is disabled on the page.)
18757      * @method isLocked
18758      * @return {boolean} true if this obj or all drag/drop is locked, else
18759      * false
18760      */
18761     isLocked: function() {
18762         return (this.DDM.isLocked() || this.locked);
18763     },
18764
18765     /**
18766      * Fired when this object is clicked
18767      * @method handleMouseDown
18768      * @param {Event} e
18769      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18770      * @private
18771      */
18772     handleMouseDown: function(e, oDD){
18773      
18774         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18775             //Roo.log('not touch/ button !=0');
18776             return;
18777         }
18778         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18779             return; // double touch..
18780         }
18781         
18782
18783         if (this.isLocked()) {
18784             //Roo.log('locked');
18785             return;
18786         }
18787
18788         this.DDM.refreshCache(this.groups);
18789 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18790         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18791         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18792             //Roo.log('no outer handes or not over target');
18793                 // do nothing.
18794         } else {
18795 //            Roo.log('check validator');
18796             if (this.clickValidator(e)) {
18797 //                Roo.log('validate success');
18798                 // set the initial element position
18799                 this.setStartPosition();
18800
18801
18802                 this.b4MouseDown(e);
18803                 this.onMouseDown(e);
18804
18805                 this.DDM.handleMouseDown(e, this);
18806
18807                 this.DDM.stopEvent(e);
18808             } else {
18809
18810
18811             }
18812         }
18813     },
18814
18815     clickValidator: function(e) {
18816         var target = e.getTarget();
18817         return ( this.isValidHandleChild(target) &&
18818                     (this.id == this.handleElId ||
18819                         this.DDM.handleWasClicked(target, this.id)) );
18820     },
18821
18822     /**
18823      * Allows you to specify a tag name that should not start a drag operation
18824      * when clicked.  This is designed to facilitate embedding links within a
18825      * drag handle that do something other than start the drag.
18826      * @method addInvalidHandleType
18827      * @param {string} tagName the type of element to exclude
18828      */
18829     addInvalidHandleType: function(tagName) {
18830         var type = tagName.toUpperCase();
18831         this.invalidHandleTypes[type] = type;
18832     },
18833
18834     /**
18835      * Lets you to specify an element id for a child of a drag handle
18836      * that should not initiate a drag
18837      * @method addInvalidHandleId
18838      * @param {string} id the element id of the element you wish to ignore
18839      */
18840     addInvalidHandleId: function(id) {
18841         if (typeof id !== "string") {
18842             id = Roo.id(id);
18843         }
18844         this.invalidHandleIds[id] = id;
18845     },
18846
18847     /**
18848      * Lets you specify a css class of elements that will not initiate a drag
18849      * @method addInvalidHandleClass
18850      * @param {string} cssClass the class of the elements you wish to ignore
18851      */
18852     addInvalidHandleClass: function(cssClass) {
18853         this.invalidHandleClasses.push(cssClass);
18854     },
18855
18856     /**
18857      * Unsets an excluded tag name set by addInvalidHandleType
18858      * @method removeInvalidHandleType
18859      * @param {string} tagName the type of element to unexclude
18860      */
18861     removeInvalidHandleType: function(tagName) {
18862         var type = tagName.toUpperCase();
18863         // this.invalidHandleTypes[type] = null;
18864         delete this.invalidHandleTypes[type];
18865     },
18866
18867     /**
18868      * Unsets an invalid handle id
18869      * @method removeInvalidHandleId
18870      * @param {string} id the id of the element to re-enable
18871      */
18872     removeInvalidHandleId: function(id) {
18873         if (typeof id !== "string") {
18874             id = Roo.id(id);
18875         }
18876         delete this.invalidHandleIds[id];
18877     },
18878
18879     /**
18880      * Unsets an invalid css class
18881      * @method removeInvalidHandleClass
18882      * @param {string} cssClass the class of the element(s) you wish to
18883      * re-enable
18884      */
18885     removeInvalidHandleClass: function(cssClass) {
18886         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
18887             if (this.invalidHandleClasses[i] == cssClass) {
18888                 delete this.invalidHandleClasses[i];
18889             }
18890         }
18891     },
18892
18893     /**
18894      * Checks the tag exclusion list to see if this click should be ignored
18895      * @method isValidHandleChild
18896      * @param {HTMLElement} node the HTMLElement to evaluate
18897      * @return {boolean} true if this is a valid tag type, false if not
18898      */
18899     isValidHandleChild: function(node) {
18900
18901         var valid = true;
18902         // var n = (node.nodeName == "#text") ? node.parentNode : node;
18903         var nodeName;
18904         try {
18905             nodeName = node.nodeName.toUpperCase();
18906         } catch(e) {
18907             nodeName = node.nodeName;
18908         }
18909         valid = valid && !this.invalidHandleTypes[nodeName];
18910         valid = valid && !this.invalidHandleIds[node.id];
18911
18912         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
18913             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
18914         }
18915
18916
18917         return valid;
18918
18919     },
18920
18921     /**
18922      * Create the array of horizontal tick marks if an interval was specified
18923      * in setXConstraint().
18924      * @method setXTicks
18925      * @private
18926      */
18927     setXTicks: function(iStartX, iTickSize) {
18928         this.xTicks = [];
18929         this.xTickSize = iTickSize;
18930
18931         var tickMap = {};
18932
18933         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
18934             if (!tickMap[i]) {
18935                 this.xTicks[this.xTicks.length] = i;
18936                 tickMap[i] = true;
18937             }
18938         }
18939
18940         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
18941             if (!tickMap[i]) {
18942                 this.xTicks[this.xTicks.length] = i;
18943                 tickMap[i] = true;
18944             }
18945         }
18946
18947         this.xTicks.sort(this.DDM.numericSort) ;
18948     },
18949
18950     /**
18951      * Create the array of vertical tick marks if an interval was specified in
18952      * setYConstraint().
18953      * @method setYTicks
18954      * @private
18955      */
18956     setYTicks: function(iStartY, iTickSize) {
18957         this.yTicks = [];
18958         this.yTickSize = iTickSize;
18959
18960         var tickMap = {};
18961
18962         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
18963             if (!tickMap[i]) {
18964                 this.yTicks[this.yTicks.length] = i;
18965                 tickMap[i] = true;
18966             }
18967         }
18968
18969         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
18970             if (!tickMap[i]) {
18971                 this.yTicks[this.yTicks.length] = i;
18972                 tickMap[i] = true;
18973             }
18974         }
18975
18976         this.yTicks.sort(this.DDM.numericSort) ;
18977     },
18978
18979     /**
18980      * By default, the element can be dragged any place on the screen.  Use
18981      * this method to limit the horizontal travel of the element.  Pass in
18982      * 0,0 for the parameters if you want to lock the drag to the y axis.
18983      * @method setXConstraint
18984      * @param {int} iLeft the number of pixels the element can move to the left
18985      * @param {int} iRight the number of pixels the element can move to the
18986      * right
18987      * @param {int} iTickSize optional parameter for specifying that the
18988      * element
18989      * should move iTickSize pixels at a time.
18990      */
18991     setXConstraint: function(iLeft, iRight, iTickSize) {
18992         this.leftConstraint = iLeft;
18993         this.rightConstraint = iRight;
18994
18995         this.minX = this.initPageX - iLeft;
18996         this.maxX = this.initPageX + iRight;
18997         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
18998
18999         this.constrainX = true;
19000     },
19001
19002     /**
19003      * Clears any constraints applied to this instance.  Also clears ticks
19004      * since they can't exist independent of a constraint at this time.
19005      * @method clearConstraints
19006      */
19007     clearConstraints: function() {
19008         this.constrainX = false;
19009         this.constrainY = false;
19010         this.clearTicks();
19011     },
19012
19013     /**
19014      * Clears any tick interval defined for this instance
19015      * @method clearTicks
19016      */
19017     clearTicks: function() {
19018         this.xTicks = null;
19019         this.yTicks = null;
19020         this.xTickSize = 0;
19021         this.yTickSize = 0;
19022     },
19023
19024     /**
19025      * By default, the element can be dragged any place on the screen.  Set
19026      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19027      * parameters if you want to lock the drag to the x axis.
19028      * @method setYConstraint
19029      * @param {int} iUp the number of pixels the element can move up
19030      * @param {int} iDown the number of pixels the element can move down
19031      * @param {int} iTickSize optional parameter for specifying that the
19032      * element should move iTickSize pixels at a time.
19033      */
19034     setYConstraint: function(iUp, iDown, iTickSize) {
19035         this.topConstraint = iUp;
19036         this.bottomConstraint = iDown;
19037
19038         this.minY = this.initPageY - iUp;
19039         this.maxY = this.initPageY + iDown;
19040         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19041
19042         this.constrainY = true;
19043
19044     },
19045
19046     /**
19047      * resetConstraints must be called if you manually reposition a dd element.
19048      * @method resetConstraints
19049      * @param {boolean} maintainOffset
19050      */
19051     resetConstraints: function() {
19052
19053
19054         // Maintain offsets if necessary
19055         if (this.initPageX || this.initPageX === 0) {
19056             // figure out how much this thing has moved
19057             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19058             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19059
19060             this.setInitPosition(dx, dy);
19061
19062         // This is the first time we have detected the element's position
19063         } else {
19064             this.setInitPosition();
19065         }
19066
19067         if (this.constrainX) {
19068             this.setXConstraint( this.leftConstraint,
19069                                  this.rightConstraint,
19070                                  this.xTickSize        );
19071         }
19072
19073         if (this.constrainY) {
19074             this.setYConstraint( this.topConstraint,
19075                                  this.bottomConstraint,
19076                                  this.yTickSize         );
19077         }
19078     },
19079
19080     /**
19081      * Normally the drag element is moved pixel by pixel, but we can specify
19082      * that it move a number of pixels at a time.  This method resolves the
19083      * location when we have it set up like this.
19084      * @method getTick
19085      * @param {int} val where we want to place the object
19086      * @param {int[]} tickArray sorted array of valid points
19087      * @return {int} the closest tick
19088      * @private
19089      */
19090     getTick: function(val, tickArray) {
19091
19092         if (!tickArray) {
19093             // If tick interval is not defined, it is effectively 1 pixel,
19094             // so we return the value passed to us.
19095             return val;
19096         } else if (tickArray[0] >= val) {
19097             // The value is lower than the first tick, so we return the first
19098             // tick.
19099             return tickArray[0];
19100         } else {
19101             for (var i=0, len=tickArray.length; i<len; ++i) {
19102                 var next = i + 1;
19103                 if (tickArray[next] && tickArray[next] >= val) {
19104                     var diff1 = val - tickArray[i];
19105                     var diff2 = tickArray[next] - val;
19106                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19107                 }
19108             }
19109
19110             // The value is larger than the last tick, so we return the last
19111             // tick.
19112             return tickArray[tickArray.length - 1];
19113         }
19114     },
19115
19116     /**
19117      * toString method
19118      * @method toString
19119      * @return {string} string representation of the dd obj
19120      */
19121     toString: function() {
19122         return ("DragDrop " + this.id);
19123     }
19124
19125 });
19126
19127 })();
19128 /*
19129  * Based on:
19130  * Ext JS Library 1.1.1
19131  * Copyright(c) 2006-2007, Ext JS, LLC.
19132  *
19133  * Originally Released Under LGPL - original licence link has changed is not relivant.
19134  *
19135  * Fork - LGPL
19136  * <script type="text/javascript">
19137  */
19138
19139
19140 /**
19141  * The drag and drop utility provides a framework for building drag and drop
19142  * applications.  In addition to enabling drag and drop for specific elements,
19143  * the drag and drop elements are tracked by the manager class, and the
19144  * interactions between the various elements are tracked during the drag and
19145  * the implementing code is notified about these important moments.
19146  */
19147
19148 // Only load the library once.  Rewriting the manager class would orphan
19149 // existing drag and drop instances.
19150 if (!Roo.dd.DragDropMgr) {
19151
19152 /**
19153  * @class Roo.dd.DragDropMgr
19154  * DragDropMgr is a singleton that tracks the element interaction for
19155  * all DragDrop items in the window.  Generally, you will not call
19156  * this class directly, but it does have helper methods that could
19157  * be useful in your DragDrop implementations.
19158  * @singleton
19159  */
19160 Roo.dd.DragDropMgr = function() {
19161
19162     var Event = Roo.EventManager;
19163
19164     return {
19165
19166         /**
19167          * Two dimensional Array of registered DragDrop objects.  The first
19168          * dimension is the DragDrop item group, the second the DragDrop
19169          * object.
19170          * @property ids
19171          * @type {string: string}
19172          * @private
19173          * @static
19174          */
19175         ids: {},
19176
19177         /**
19178          * Array of element ids defined as drag handles.  Used to determine
19179          * if the element that generated the mousedown event is actually the
19180          * handle and not the html element itself.
19181          * @property handleIds
19182          * @type {string: string}
19183          * @private
19184          * @static
19185          */
19186         handleIds: {},
19187
19188         /**
19189          * the DragDrop object that is currently being dragged
19190          * @property dragCurrent
19191          * @type DragDrop
19192          * @private
19193          * @static
19194          **/
19195         dragCurrent: null,
19196
19197         /**
19198          * the DragDrop object(s) that are being hovered over
19199          * @property dragOvers
19200          * @type Array
19201          * @private
19202          * @static
19203          */
19204         dragOvers: {},
19205
19206         /**
19207          * the X distance between the cursor and the object being dragged
19208          * @property deltaX
19209          * @type int
19210          * @private
19211          * @static
19212          */
19213         deltaX: 0,
19214
19215         /**
19216          * the Y distance between the cursor and the object being dragged
19217          * @property deltaY
19218          * @type int
19219          * @private
19220          * @static
19221          */
19222         deltaY: 0,
19223
19224         /**
19225          * Flag to determine if we should prevent the default behavior of the
19226          * events we define. By default this is true, but this can be set to
19227          * false if you need the default behavior (not recommended)
19228          * @property preventDefault
19229          * @type boolean
19230          * @static
19231          */
19232         preventDefault: true,
19233
19234         /**
19235          * Flag to determine if we should stop the propagation of the events
19236          * we generate. This is true by default but you may want to set it to
19237          * false if the html element contains other features that require the
19238          * mouse click.
19239          * @property stopPropagation
19240          * @type boolean
19241          * @static
19242          */
19243         stopPropagation: true,
19244
19245         /**
19246          * Internal flag that is set to true when drag and drop has been
19247          * intialized
19248          * @property initialized
19249          * @private
19250          * @static
19251          */
19252         initalized: false,
19253
19254         /**
19255          * All drag and drop can be disabled.
19256          * @property locked
19257          * @private
19258          * @static
19259          */
19260         locked: false,
19261
19262         /**
19263          * Called the first time an element is registered.
19264          * @method init
19265          * @private
19266          * @static
19267          */
19268         init: function() {
19269             this.initialized = true;
19270         },
19271
19272         /**
19273          * In point mode, drag and drop interaction is defined by the
19274          * location of the cursor during the drag/drop
19275          * @property POINT
19276          * @type int
19277          * @static
19278          */
19279         POINT: 0,
19280
19281         /**
19282          * In intersect mode, drag and drop interactio nis defined by the
19283          * overlap of two or more drag and drop objects.
19284          * @property INTERSECT
19285          * @type int
19286          * @static
19287          */
19288         INTERSECT: 1,
19289
19290         /**
19291          * The current drag and drop mode.  Default: POINT
19292          * @property mode
19293          * @type int
19294          * @static
19295          */
19296         mode: 0,
19297
19298         /**
19299          * Runs method on all drag and drop objects
19300          * @method _execOnAll
19301          * @private
19302          * @static
19303          */
19304         _execOnAll: function(sMethod, args) {
19305             for (var i in this.ids) {
19306                 for (var j in this.ids[i]) {
19307                     var oDD = this.ids[i][j];
19308                     if (! this.isTypeOfDD(oDD)) {
19309                         continue;
19310                     }
19311                     oDD[sMethod].apply(oDD, args);
19312                 }
19313             }
19314         },
19315
19316         /**
19317          * Drag and drop initialization.  Sets up the global event handlers
19318          * @method _onLoad
19319          * @private
19320          * @static
19321          */
19322         _onLoad: function() {
19323
19324             this.init();
19325
19326             if (!Roo.isTouch) {
19327                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19328                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19329             }
19330             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19331             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19332             
19333             Event.on(window,   "unload",    this._onUnload, this, true);
19334             Event.on(window,   "resize",    this._onResize, this, true);
19335             // Event.on(window,   "mouseout",    this._test);
19336
19337         },
19338
19339         /**
19340          * Reset constraints on all drag and drop objs
19341          * @method _onResize
19342          * @private
19343          * @static
19344          */
19345         _onResize: function(e) {
19346             this._execOnAll("resetConstraints", []);
19347         },
19348
19349         /**
19350          * Lock all drag and drop functionality
19351          * @method lock
19352          * @static
19353          */
19354         lock: function() { this.locked = true; },
19355
19356         /**
19357          * Unlock all drag and drop functionality
19358          * @method unlock
19359          * @static
19360          */
19361         unlock: function() { this.locked = false; },
19362
19363         /**
19364          * Is drag and drop locked?
19365          * @method isLocked
19366          * @return {boolean} True if drag and drop is locked, false otherwise.
19367          * @static
19368          */
19369         isLocked: function() { return this.locked; },
19370
19371         /**
19372          * Location cache that is set for all drag drop objects when a drag is
19373          * initiated, cleared when the drag is finished.
19374          * @property locationCache
19375          * @private
19376          * @static
19377          */
19378         locationCache: {},
19379
19380         /**
19381          * Set useCache to false if you want to force object the lookup of each
19382          * drag and drop linked element constantly during a drag.
19383          * @property useCache
19384          * @type boolean
19385          * @static
19386          */
19387         useCache: true,
19388
19389         /**
19390          * The number of pixels that the mouse needs to move after the
19391          * mousedown before the drag is initiated.  Default=3;
19392          * @property clickPixelThresh
19393          * @type int
19394          * @static
19395          */
19396         clickPixelThresh: 3,
19397
19398         /**
19399          * The number of milliseconds after the mousedown event to initiate the
19400          * drag if we don't get a mouseup event. Default=1000
19401          * @property clickTimeThresh
19402          * @type int
19403          * @static
19404          */
19405         clickTimeThresh: 350,
19406
19407         /**
19408          * Flag that indicates that either the drag pixel threshold or the
19409          * mousdown time threshold has been met
19410          * @property dragThreshMet
19411          * @type boolean
19412          * @private
19413          * @static
19414          */
19415         dragThreshMet: false,
19416
19417         /**
19418          * Timeout used for the click time threshold
19419          * @property clickTimeout
19420          * @type Object
19421          * @private
19422          * @static
19423          */
19424         clickTimeout: null,
19425
19426         /**
19427          * The X position of the mousedown event stored for later use when a
19428          * drag threshold is met.
19429          * @property startX
19430          * @type int
19431          * @private
19432          * @static
19433          */
19434         startX: 0,
19435
19436         /**
19437          * The Y position of the mousedown event stored for later use when a
19438          * drag threshold is met.
19439          * @property startY
19440          * @type int
19441          * @private
19442          * @static
19443          */
19444         startY: 0,
19445
19446         /**
19447          * Each DragDrop instance must be registered with the DragDropMgr.
19448          * This is executed in DragDrop.init()
19449          * @method regDragDrop
19450          * @param {DragDrop} oDD the DragDrop object to register
19451          * @param {String} sGroup the name of the group this element belongs to
19452          * @static
19453          */
19454         regDragDrop: function(oDD, sGroup) {
19455             if (!this.initialized) { this.init(); }
19456
19457             if (!this.ids[sGroup]) {
19458                 this.ids[sGroup] = {};
19459             }
19460             this.ids[sGroup][oDD.id] = oDD;
19461         },
19462
19463         /**
19464          * Removes the supplied dd instance from the supplied group. Executed
19465          * by DragDrop.removeFromGroup, so don't call this function directly.
19466          * @method removeDDFromGroup
19467          * @private
19468          * @static
19469          */
19470         removeDDFromGroup: function(oDD, sGroup) {
19471             if (!this.ids[sGroup]) {
19472                 this.ids[sGroup] = {};
19473             }
19474
19475             var obj = this.ids[sGroup];
19476             if (obj && obj[oDD.id]) {
19477                 delete obj[oDD.id];
19478             }
19479         },
19480
19481         /**
19482          * Unregisters a drag and drop item.  This is executed in
19483          * DragDrop.unreg, use that method instead of calling this directly.
19484          * @method _remove
19485          * @private
19486          * @static
19487          */
19488         _remove: function(oDD) {
19489             for (var g in oDD.groups) {
19490                 if (g && this.ids[g][oDD.id]) {
19491                     delete this.ids[g][oDD.id];
19492                 }
19493             }
19494             delete this.handleIds[oDD.id];
19495         },
19496
19497         /**
19498          * Each DragDrop handle element must be registered.  This is done
19499          * automatically when executing DragDrop.setHandleElId()
19500          * @method regHandle
19501          * @param {String} sDDId the DragDrop id this element is a handle for
19502          * @param {String} sHandleId the id of the element that is the drag
19503          * handle
19504          * @static
19505          */
19506         regHandle: function(sDDId, sHandleId) {
19507             if (!this.handleIds[sDDId]) {
19508                 this.handleIds[sDDId] = {};
19509             }
19510             this.handleIds[sDDId][sHandleId] = sHandleId;
19511         },
19512
19513         /**
19514          * Utility function to determine if a given element has been
19515          * registered as a drag drop item.
19516          * @method isDragDrop
19517          * @param {String} id the element id to check
19518          * @return {boolean} true if this element is a DragDrop item,
19519          * false otherwise
19520          * @static
19521          */
19522         isDragDrop: function(id) {
19523             return ( this.getDDById(id) ) ? true : false;
19524         },
19525
19526         /**
19527          * Returns the drag and drop instances that are in all groups the
19528          * passed in instance belongs to.
19529          * @method getRelated
19530          * @param {DragDrop} p_oDD the obj to get related data for
19531          * @param {boolean} bTargetsOnly if true, only return targetable objs
19532          * @return {DragDrop[]} the related instances
19533          * @static
19534          */
19535         getRelated: function(p_oDD, bTargetsOnly) {
19536             var oDDs = [];
19537             for (var i in p_oDD.groups) {
19538                 for (j in this.ids[i]) {
19539                     var dd = this.ids[i][j];
19540                     if (! this.isTypeOfDD(dd)) {
19541                         continue;
19542                     }
19543                     if (!bTargetsOnly || dd.isTarget) {
19544                         oDDs[oDDs.length] = dd;
19545                     }
19546                 }
19547             }
19548
19549             return oDDs;
19550         },
19551
19552         /**
19553          * Returns true if the specified dd target is a legal target for
19554          * the specifice drag obj
19555          * @method isLegalTarget
19556          * @param {DragDrop} the drag obj
19557          * @param {DragDrop} the target
19558          * @return {boolean} true if the target is a legal target for the
19559          * dd obj
19560          * @static
19561          */
19562         isLegalTarget: function (oDD, oTargetDD) {
19563             var targets = this.getRelated(oDD, true);
19564             for (var i=0, len=targets.length;i<len;++i) {
19565                 if (targets[i].id == oTargetDD.id) {
19566                     return true;
19567                 }
19568             }
19569
19570             return false;
19571         },
19572
19573         /**
19574          * My goal is to be able to transparently determine if an object is
19575          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19576          * returns "object", oDD.constructor.toString() always returns
19577          * "DragDrop" and not the name of the subclass.  So for now it just
19578          * evaluates a well-known variable in DragDrop.
19579          * @method isTypeOfDD
19580          * @param {Object} the object to evaluate
19581          * @return {boolean} true if typeof oDD = DragDrop
19582          * @static
19583          */
19584         isTypeOfDD: function (oDD) {
19585             return (oDD && oDD.__ygDragDrop);
19586         },
19587
19588         /**
19589          * Utility function to determine if a given element has been
19590          * registered as a drag drop handle for the given Drag Drop object.
19591          * @method isHandle
19592          * @param {String} id the element id to check
19593          * @return {boolean} true if this element is a DragDrop handle, false
19594          * otherwise
19595          * @static
19596          */
19597         isHandle: function(sDDId, sHandleId) {
19598             return ( this.handleIds[sDDId] &&
19599                             this.handleIds[sDDId][sHandleId] );
19600         },
19601
19602         /**
19603          * Returns the DragDrop instance for a given id
19604          * @method getDDById
19605          * @param {String} id the id of the DragDrop object
19606          * @return {DragDrop} the drag drop object, null if it is not found
19607          * @static
19608          */
19609         getDDById: function(id) {
19610             for (var i in this.ids) {
19611                 if (this.ids[i][id]) {
19612                     return this.ids[i][id];
19613                 }
19614             }
19615             return null;
19616         },
19617
19618         /**
19619          * Fired after a registered DragDrop object gets the mousedown event.
19620          * Sets up the events required to track the object being dragged
19621          * @method handleMouseDown
19622          * @param {Event} e the event
19623          * @param oDD the DragDrop object being dragged
19624          * @private
19625          * @static
19626          */
19627         handleMouseDown: function(e, oDD) {
19628             if(Roo.QuickTips){
19629                 Roo.QuickTips.disable();
19630             }
19631             this.currentTarget = e.getTarget();
19632
19633             this.dragCurrent = oDD;
19634
19635             var el = oDD.getEl();
19636
19637             // track start position
19638             this.startX = e.getPageX();
19639             this.startY = e.getPageY();
19640
19641             this.deltaX = this.startX - el.offsetLeft;
19642             this.deltaY = this.startY - el.offsetTop;
19643
19644             this.dragThreshMet = false;
19645
19646             this.clickTimeout = setTimeout(
19647                     function() {
19648                         var DDM = Roo.dd.DDM;
19649                         DDM.startDrag(DDM.startX, DDM.startY);
19650                     },
19651                     this.clickTimeThresh );
19652         },
19653
19654         /**
19655          * Fired when either the drag pixel threshol or the mousedown hold
19656          * time threshold has been met.
19657          * @method startDrag
19658          * @param x {int} the X position of the original mousedown
19659          * @param y {int} the Y position of the original mousedown
19660          * @static
19661          */
19662         startDrag: function(x, y) {
19663             clearTimeout(this.clickTimeout);
19664             if (this.dragCurrent) {
19665                 this.dragCurrent.b4StartDrag(x, y);
19666                 this.dragCurrent.startDrag(x, y);
19667             }
19668             this.dragThreshMet = true;
19669         },
19670
19671         /**
19672          * Internal function to handle the mouseup event.  Will be invoked
19673          * from the context of the document.
19674          * @method handleMouseUp
19675          * @param {Event} e the event
19676          * @private
19677          * @static
19678          */
19679         handleMouseUp: function(e) {
19680
19681             if(Roo.QuickTips){
19682                 Roo.QuickTips.enable();
19683             }
19684             if (! this.dragCurrent) {
19685                 return;
19686             }
19687
19688             clearTimeout(this.clickTimeout);
19689
19690             if (this.dragThreshMet) {
19691                 this.fireEvents(e, true);
19692             } else {
19693             }
19694
19695             this.stopDrag(e);
19696
19697             this.stopEvent(e);
19698         },
19699
19700         /**
19701          * Utility to stop event propagation and event default, if these
19702          * features are turned on.
19703          * @method stopEvent
19704          * @param {Event} e the event as returned by this.getEvent()
19705          * @static
19706          */
19707         stopEvent: function(e){
19708             if(this.stopPropagation) {
19709                 e.stopPropagation();
19710             }
19711
19712             if (this.preventDefault) {
19713                 e.preventDefault();
19714             }
19715         },
19716
19717         /**
19718          * Internal function to clean up event handlers after the drag
19719          * operation is complete
19720          * @method stopDrag
19721          * @param {Event} e the event
19722          * @private
19723          * @static
19724          */
19725         stopDrag: function(e) {
19726             // Fire the drag end event for the item that was dragged
19727             if (this.dragCurrent) {
19728                 if (this.dragThreshMet) {
19729                     this.dragCurrent.b4EndDrag(e);
19730                     this.dragCurrent.endDrag(e);
19731                 }
19732
19733                 this.dragCurrent.onMouseUp(e);
19734             }
19735
19736             this.dragCurrent = null;
19737             this.dragOvers = {};
19738         },
19739
19740         /**
19741          * Internal function to handle the mousemove event.  Will be invoked
19742          * from the context of the html element.
19743          *
19744          * @TODO figure out what we can do about mouse events lost when the
19745          * user drags objects beyond the window boundary.  Currently we can
19746          * detect this in internet explorer by verifying that the mouse is
19747          * down during the mousemove event.  Firefox doesn't give us the
19748          * button state on the mousemove event.
19749          * @method handleMouseMove
19750          * @param {Event} e the event
19751          * @private
19752          * @static
19753          */
19754         handleMouseMove: function(e) {
19755             if (! this.dragCurrent) {
19756                 return true;
19757             }
19758
19759             // var button = e.which || e.button;
19760
19761             // check for IE mouseup outside of page boundary
19762             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19763                 this.stopEvent(e);
19764                 return this.handleMouseUp(e);
19765             }
19766
19767             if (!this.dragThreshMet) {
19768                 var diffX = Math.abs(this.startX - e.getPageX());
19769                 var diffY = Math.abs(this.startY - e.getPageY());
19770                 if (diffX > this.clickPixelThresh ||
19771                             diffY > this.clickPixelThresh) {
19772                     this.startDrag(this.startX, this.startY);
19773                 }
19774             }
19775
19776             if (this.dragThreshMet) {
19777                 this.dragCurrent.b4Drag(e);
19778                 this.dragCurrent.onDrag(e);
19779                 if(!this.dragCurrent.moveOnly){
19780                     this.fireEvents(e, false);
19781                 }
19782             }
19783
19784             this.stopEvent(e);
19785
19786             return true;
19787         },
19788
19789         /**
19790          * Iterates over all of the DragDrop elements to find ones we are
19791          * hovering over or dropping on
19792          * @method fireEvents
19793          * @param {Event} e the event
19794          * @param {boolean} isDrop is this a drop op or a mouseover op?
19795          * @private
19796          * @static
19797          */
19798         fireEvents: function(e, isDrop) {
19799             var dc = this.dragCurrent;
19800
19801             // If the user did the mouse up outside of the window, we could
19802             // get here even though we have ended the drag.
19803             if (!dc || dc.isLocked()) {
19804                 return;
19805             }
19806
19807             var pt = e.getPoint();
19808
19809             // cache the previous dragOver array
19810             var oldOvers = [];
19811
19812             var outEvts   = [];
19813             var overEvts  = [];
19814             var dropEvts  = [];
19815             var enterEvts = [];
19816
19817             // Check to see if the object(s) we were hovering over is no longer
19818             // being hovered over so we can fire the onDragOut event
19819             for (var i in this.dragOvers) {
19820
19821                 var ddo = this.dragOvers[i];
19822
19823                 if (! this.isTypeOfDD(ddo)) {
19824                     continue;
19825                 }
19826
19827                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19828                     outEvts.push( ddo );
19829                 }
19830
19831                 oldOvers[i] = true;
19832                 delete this.dragOvers[i];
19833             }
19834
19835             for (var sGroup in dc.groups) {
19836
19837                 if ("string" != typeof sGroup) {
19838                     continue;
19839                 }
19840
19841                 for (i in this.ids[sGroup]) {
19842                     var oDD = this.ids[sGroup][i];
19843                     if (! this.isTypeOfDD(oDD)) {
19844                         continue;
19845                     }
19846
19847                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19848                         if (this.isOverTarget(pt, oDD, this.mode)) {
19849                             // look for drop interactions
19850                             if (isDrop) {
19851                                 dropEvts.push( oDD );
19852                             // look for drag enter and drag over interactions
19853                             } else {
19854
19855                                 // initial drag over: dragEnter fires
19856                                 if (!oldOvers[oDD.id]) {
19857                                     enterEvts.push( oDD );
19858                                 // subsequent drag overs: dragOver fires
19859                                 } else {
19860                                     overEvts.push( oDD );
19861                                 }
19862
19863                                 this.dragOvers[oDD.id] = oDD;
19864                             }
19865                         }
19866                     }
19867                 }
19868             }
19869
19870             if (this.mode) {
19871                 if (outEvts.length) {
19872                     dc.b4DragOut(e, outEvts);
19873                     dc.onDragOut(e, outEvts);
19874                 }
19875
19876                 if (enterEvts.length) {
19877                     dc.onDragEnter(e, enterEvts);
19878                 }
19879
19880                 if (overEvts.length) {
19881                     dc.b4DragOver(e, overEvts);
19882                     dc.onDragOver(e, overEvts);
19883                 }
19884
19885                 if (dropEvts.length) {
19886                     dc.b4DragDrop(e, dropEvts);
19887                     dc.onDragDrop(e, dropEvts);
19888                 }
19889
19890             } else {
19891                 // fire dragout events
19892                 var len = 0;
19893                 for (i=0, len=outEvts.length; i<len; ++i) {
19894                     dc.b4DragOut(e, outEvts[i].id);
19895                     dc.onDragOut(e, outEvts[i].id);
19896                 }
19897
19898                 // fire enter events
19899                 for (i=0,len=enterEvts.length; i<len; ++i) {
19900                     // dc.b4DragEnter(e, oDD.id);
19901                     dc.onDragEnter(e, enterEvts[i].id);
19902                 }
19903
19904                 // fire over events
19905                 for (i=0,len=overEvts.length; i<len; ++i) {
19906                     dc.b4DragOver(e, overEvts[i].id);
19907                     dc.onDragOver(e, overEvts[i].id);
19908                 }
19909
19910                 // fire drop events
19911                 for (i=0, len=dropEvts.length; i<len; ++i) {
19912                     dc.b4DragDrop(e, dropEvts[i].id);
19913                     dc.onDragDrop(e, dropEvts[i].id);
19914                 }
19915
19916             }
19917
19918             // notify about a drop that did not find a target
19919             if (isDrop && !dropEvts.length) {
19920                 dc.onInvalidDrop(e);
19921             }
19922
19923         },
19924
19925         /**
19926          * Helper function for getting the best match from the list of drag
19927          * and drop objects returned by the drag and drop events when we are
19928          * in INTERSECT mode.  It returns either the first object that the
19929          * cursor is over, or the object that has the greatest overlap with
19930          * the dragged element.
19931          * @method getBestMatch
19932          * @param  {DragDrop[]} dds The array of drag and drop objects
19933          * targeted
19934          * @return {DragDrop}       The best single match
19935          * @static
19936          */
19937         getBestMatch: function(dds) {
19938             var winner = null;
19939             // Return null if the input is not what we expect
19940             //if (!dds || !dds.length || dds.length == 0) {
19941                // winner = null;
19942             // If there is only one item, it wins
19943             //} else if (dds.length == 1) {
19944
19945             var len = dds.length;
19946
19947             if (len == 1) {
19948                 winner = dds[0];
19949             } else {
19950                 // Loop through the targeted items
19951                 for (var i=0; i<len; ++i) {
19952                     var dd = dds[i];
19953                     // If the cursor is over the object, it wins.  If the
19954                     // cursor is over multiple matches, the first one we come
19955                     // to wins.
19956                     if (dd.cursorIsOver) {
19957                         winner = dd;
19958                         break;
19959                     // Otherwise the object with the most overlap wins
19960                     } else {
19961                         if (!winner ||
19962                             winner.overlap.getArea() < dd.overlap.getArea()) {
19963                             winner = dd;
19964                         }
19965                     }
19966                 }
19967             }
19968
19969             return winner;
19970         },
19971
19972         /**
19973          * Refreshes the cache of the top-left and bottom-right points of the
19974          * drag and drop objects in the specified group(s).  This is in the
19975          * format that is stored in the drag and drop instance, so typical
19976          * usage is:
19977          * <code>
19978          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
19979          * </code>
19980          * Alternatively:
19981          * <code>
19982          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
19983          * </code>
19984          * @TODO this really should be an indexed array.  Alternatively this
19985          * method could accept both.
19986          * @method refreshCache
19987          * @param {Object} groups an associative array of groups to refresh
19988          * @static
19989          */
19990         refreshCache: function(groups) {
19991             for (var sGroup in groups) {
19992                 if ("string" != typeof sGroup) {
19993                     continue;
19994                 }
19995                 for (var i in this.ids[sGroup]) {
19996                     var oDD = this.ids[sGroup][i];
19997
19998                     if (this.isTypeOfDD(oDD)) {
19999                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20000                         var loc = this.getLocation(oDD);
20001                         if (loc) {
20002                             this.locationCache[oDD.id] = loc;
20003                         } else {
20004                             delete this.locationCache[oDD.id];
20005                             // this will unregister the drag and drop object if
20006                             // the element is not in a usable state
20007                             // oDD.unreg();
20008                         }
20009                     }
20010                 }
20011             }
20012         },
20013
20014         /**
20015          * This checks to make sure an element exists and is in the DOM.  The
20016          * main purpose is to handle cases where innerHTML is used to remove
20017          * drag and drop objects from the DOM.  IE provides an 'unspecified
20018          * error' when trying to access the offsetParent of such an element
20019          * @method verifyEl
20020          * @param {HTMLElement} el the element to check
20021          * @return {boolean} true if the element looks usable
20022          * @static
20023          */
20024         verifyEl: function(el) {
20025             if (el) {
20026                 var parent;
20027                 if(Roo.isIE){
20028                     try{
20029                         parent = el.offsetParent;
20030                     }catch(e){}
20031                 }else{
20032                     parent = el.offsetParent;
20033                 }
20034                 if (parent) {
20035                     return true;
20036                 }
20037             }
20038
20039             return false;
20040         },
20041
20042         /**
20043          * Returns a Region object containing the drag and drop element's position
20044          * and size, including the padding configured for it
20045          * @method getLocation
20046          * @param {DragDrop} oDD the drag and drop object to get the
20047          *                       location for
20048          * @return {Roo.lib.Region} a Region object representing the total area
20049          *                             the element occupies, including any padding
20050          *                             the instance is configured for.
20051          * @static
20052          */
20053         getLocation: function(oDD) {
20054             if (! this.isTypeOfDD(oDD)) {
20055                 return null;
20056             }
20057
20058             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20059
20060             try {
20061                 pos= Roo.lib.Dom.getXY(el);
20062             } catch (e) { }
20063
20064             if (!pos) {
20065                 return null;
20066             }
20067
20068             x1 = pos[0];
20069             x2 = x1 + el.offsetWidth;
20070             y1 = pos[1];
20071             y2 = y1 + el.offsetHeight;
20072
20073             t = y1 - oDD.padding[0];
20074             r = x2 + oDD.padding[1];
20075             b = y2 + oDD.padding[2];
20076             l = x1 - oDD.padding[3];
20077
20078             return new Roo.lib.Region( t, r, b, l );
20079         },
20080
20081         /**
20082          * Checks the cursor location to see if it over the target
20083          * @method isOverTarget
20084          * @param {Roo.lib.Point} pt The point to evaluate
20085          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20086          * @return {boolean} true if the mouse is over the target
20087          * @private
20088          * @static
20089          */
20090         isOverTarget: function(pt, oTarget, intersect) {
20091             // use cache if available
20092             var loc = this.locationCache[oTarget.id];
20093             if (!loc || !this.useCache) {
20094                 loc = this.getLocation(oTarget);
20095                 this.locationCache[oTarget.id] = loc;
20096
20097             }
20098
20099             if (!loc) {
20100                 return false;
20101             }
20102
20103             oTarget.cursorIsOver = loc.contains( pt );
20104
20105             // DragDrop is using this as a sanity check for the initial mousedown
20106             // in this case we are done.  In POINT mode, if the drag obj has no
20107             // contraints, we are also done. Otherwise we need to evaluate the
20108             // location of the target as related to the actual location of the
20109             // dragged element.
20110             var dc = this.dragCurrent;
20111             if (!dc || !dc.getTargetCoord ||
20112                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20113                 return oTarget.cursorIsOver;
20114             }
20115
20116             oTarget.overlap = null;
20117
20118             // Get the current location of the drag element, this is the
20119             // location of the mouse event less the delta that represents
20120             // where the original mousedown happened on the element.  We
20121             // need to consider constraints and ticks as well.
20122             var pos = dc.getTargetCoord(pt.x, pt.y);
20123
20124             var el = dc.getDragEl();
20125             var curRegion = new Roo.lib.Region( pos.y,
20126                                                    pos.x + el.offsetWidth,
20127                                                    pos.y + el.offsetHeight,
20128                                                    pos.x );
20129
20130             var overlap = curRegion.intersect(loc);
20131
20132             if (overlap) {
20133                 oTarget.overlap = overlap;
20134                 return (intersect) ? true : oTarget.cursorIsOver;
20135             } else {
20136                 return false;
20137             }
20138         },
20139
20140         /**
20141          * unload event handler
20142          * @method _onUnload
20143          * @private
20144          * @static
20145          */
20146         _onUnload: function(e, me) {
20147             Roo.dd.DragDropMgr.unregAll();
20148         },
20149
20150         /**
20151          * Cleans up the drag and drop events and objects.
20152          * @method unregAll
20153          * @private
20154          * @static
20155          */
20156         unregAll: function() {
20157
20158             if (this.dragCurrent) {
20159                 this.stopDrag();
20160                 this.dragCurrent = null;
20161             }
20162
20163             this._execOnAll("unreg", []);
20164
20165             for (i in this.elementCache) {
20166                 delete this.elementCache[i];
20167             }
20168
20169             this.elementCache = {};
20170             this.ids = {};
20171         },
20172
20173         /**
20174          * A cache of DOM elements
20175          * @property elementCache
20176          * @private
20177          * @static
20178          */
20179         elementCache: {},
20180
20181         /**
20182          * Get the wrapper for the DOM element specified
20183          * @method getElWrapper
20184          * @param {String} id the id of the element to get
20185          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20186          * @private
20187          * @deprecated This wrapper isn't that useful
20188          * @static
20189          */
20190         getElWrapper: function(id) {
20191             var oWrapper = this.elementCache[id];
20192             if (!oWrapper || !oWrapper.el) {
20193                 oWrapper = this.elementCache[id] =
20194                     new this.ElementWrapper(Roo.getDom(id));
20195             }
20196             return oWrapper;
20197         },
20198
20199         /**
20200          * Returns the actual DOM element
20201          * @method getElement
20202          * @param {String} id the id of the elment to get
20203          * @return {Object} The element
20204          * @deprecated use Roo.getDom instead
20205          * @static
20206          */
20207         getElement: function(id) {
20208             return Roo.getDom(id);
20209         },
20210
20211         /**
20212          * Returns the style property for the DOM element (i.e.,
20213          * document.getElById(id).style)
20214          * @method getCss
20215          * @param {String} id the id of the elment to get
20216          * @return {Object} The style property of the element
20217          * @deprecated use Roo.getDom instead
20218          * @static
20219          */
20220         getCss: function(id) {
20221             var el = Roo.getDom(id);
20222             return (el) ? el.style : null;
20223         },
20224
20225         /**
20226          * Inner class for cached elements
20227          * @class DragDropMgr.ElementWrapper
20228          * @for DragDropMgr
20229          * @private
20230          * @deprecated
20231          */
20232         ElementWrapper: function(el) {
20233                 /**
20234                  * The element
20235                  * @property el
20236                  */
20237                 this.el = el || null;
20238                 /**
20239                  * The element id
20240                  * @property id
20241                  */
20242                 this.id = this.el && el.id;
20243                 /**
20244                  * A reference to the style property
20245                  * @property css
20246                  */
20247                 this.css = this.el && el.style;
20248             },
20249
20250         /**
20251          * Returns the X position of an html element
20252          * @method getPosX
20253          * @param el the element for which to get the position
20254          * @return {int} the X coordinate
20255          * @for DragDropMgr
20256          * @deprecated use Roo.lib.Dom.getX instead
20257          * @static
20258          */
20259         getPosX: function(el) {
20260             return Roo.lib.Dom.getX(el);
20261         },
20262
20263         /**
20264          * Returns the Y position of an html element
20265          * @method getPosY
20266          * @param el the element for which to get the position
20267          * @return {int} the Y coordinate
20268          * @deprecated use Roo.lib.Dom.getY instead
20269          * @static
20270          */
20271         getPosY: function(el) {
20272             return Roo.lib.Dom.getY(el);
20273         },
20274
20275         /**
20276          * Swap two nodes.  In IE, we use the native method, for others we
20277          * emulate the IE behavior
20278          * @method swapNode
20279          * @param n1 the first node to swap
20280          * @param n2 the other node to swap
20281          * @static
20282          */
20283         swapNode: function(n1, n2) {
20284             if (n1.swapNode) {
20285                 n1.swapNode(n2);
20286             } else {
20287                 var p = n2.parentNode;
20288                 var s = n2.nextSibling;
20289
20290                 if (s == n1) {
20291                     p.insertBefore(n1, n2);
20292                 } else if (n2 == n1.nextSibling) {
20293                     p.insertBefore(n2, n1);
20294                 } else {
20295                     n1.parentNode.replaceChild(n2, n1);
20296                     p.insertBefore(n1, s);
20297                 }
20298             }
20299         },
20300
20301         /**
20302          * Returns the current scroll position
20303          * @method getScroll
20304          * @private
20305          * @static
20306          */
20307         getScroll: function () {
20308             var t, l, dde=document.documentElement, db=document.body;
20309             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20310                 t = dde.scrollTop;
20311                 l = dde.scrollLeft;
20312             } else if (db) {
20313                 t = db.scrollTop;
20314                 l = db.scrollLeft;
20315             } else {
20316
20317             }
20318             return { top: t, left: l };
20319         },
20320
20321         /**
20322          * Returns the specified element style property
20323          * @method getStyle
20324          * @param {HTMLElement} el          the element
20325          * @param {string}      styleProp   the style property
20326          * @return {string} The value of the style property
20327          * @deprecated use Roo.lib.Dom.getStyle
20328          * @static
20329          */
20330         getStyle: function(el, styleProp) {
20331             return Roo.fly(el).getStyle(styleProp);
20332         },
20333
20334         /**
20335          * Gets the scrollTop
20336          * @method getScrollTop
20337          * @return {int} the document's scrollTop
20338          * @static
20339          */
20340         getScrollTop: function () { return this.getScroll().top; },
20341
20342         /**
20343          * Gets the scrollLeft
20344          * @method getScrollLeft
20345          * @return {int} the document's scrollTop
20346          * @static
20347          */
20348         getScrollLeft: function () { return this.getScroll().left; },
20349
20350         /**
20351          * Sets the x/y position of an element to the location of the
20352          * target element.
20353          * @method moveToEl
20354          * @param {HTMLElement} moveEl      The element to move
20355          * @param {HTMLElement} targetEl    The position reference element
20356          * @static
20357          */
20358         moveToEl: function (moveEl, targetEl) {
20359             var aCoord = Roo.lib.Dom.getXY(targetEl);
20360             Roo.lib.Dom.setXY(moveEl, aCoord);
20361         },
20362
20363         /**
20364          * Numeric array sort function
20365          * @method numericSort
20366          * @static
20367          */
20368         numericSort: function(a, b) { return (a - b); },
20369
20370         /**
20371          * Internal counter
20372          * @property _timeoutCount
20373          * @private
20374          * @static
20375          */
20376         _timeoutCount: 0,
20377
20378         /**
20379          * Trying to make the load order less important.  Without this we get
20380          * an error if this file is loaded before the Event Utility.
20381          * @method _addListeners
20382          * @private
20383          * @static
20384          */
20385         _addListeners: function() {
20386             var DDM = Roo.dd.DDM;
20387             if ( Roo.lib.Event && document ) {
20388                 DDM._onLoad();
20389             } else {
20390                 if (DDM._timeoutCount > 2000) {
20391                 } else {
20392                     setTimeout(DDM._addListeners, 10);
20393                     if (document && document.body) {
20394                         DDM._timeoutCount += 1;
20395                     }
20396                 }
20397             }
20398         },
20399
20400         /**
20401          * Recursively searches the immediate parent and all child nodes for
20402          * the handle element in order to determine wheter or not it was
20403          * clicked.
20404          * @method handleWasClicked
20405          * @param node the html element to inspect
20406          * @static
20407          */
20408         handleWasClicked: function(node, id) {
20409             if (this.isHandle(id, node.id)) {
20410                 return true;
20411             } else {
20412                 // check to see if this is a text node child of the one we want
20413                 var p = node.parentNode;
20414
20415                 while (p) {
20416                     if (this.isHandle(id, p.id)) {
20417                         return true;
20418                     } else {
20419                         p = p.parentNode;
20420                     }
20421                 }
20422             }
20423
20424             return false;
20425         }
20426
20427     };
20428
20429 }();
20430
20431 // shorter alias, save a few bytes
20432 Roo.dd.DDM = Roo.dd.DragDropMgr;
20433 Roo.dd.DDM._addListeners();
20434
20435 }/*
20436  * Based on:
20437  * Ext JS Library 1.1.1
20438  * Copyright(c) 2006-2007, Ext JS, LLC.
20439  *
20440  * Originally Released Under LGPL - original licence link has changed is not relivant.
20441  *
20442  * Fork - LGPL
20443  * <script type="text/javascript">
20444  */
20445
20446 /**
20447  * @class Roo.dd.DD
20448  * A DragDrop implementation where the linked element follows the
20449  * mouse cursor during a drag.
20450  * @extends Roo.dd.DragDrop
20451  * @constructor
20452  * @param {String} id the id of the linked element
20453  * @param {String} sGroup the group of related DragDrop items
20454  * @param {object} config an object containing configurable attributes
20455  *                Valid properties for DD:
20456  *                    scroll
20457  */
20458 Roo.dd.DD = function(id, sGroup, config) {
20459     if (id) {
20460         this.init(id, sGroup, config);
20461     }
20462 };
20463
20464 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20465
20466     /**
20467      * When set to true, the utility automatically tries to scroll the browser
20468      * window wehn a drag and drop element is dragged near the viewport boundary.
20469      * Defaults to true.
20470      * @property scroll
20471      * @type boolean
20472      */
20473     scroll: true,
20474
20475     /**
20476      * Sets the pointer offset to the distance between the linked element's top
20477      * left corner and the location the element was clicked
20478      * @method autoOffset
20479      * @param {int} iPageX the X coordinate of the click
20480      * @param {int} iPageY the Y coordinate of the click
20481      */
20482     autoOffset: function(iPageX, iPageY) {
20483         var x = iPageX - this.startPageX;
20484         var y = iPageY - this.startPageY;
20485         this.setDelta(x, y);
20486     },
20487
20488     /**
20489      * Sets the pointer offset.  You can call this directly to force the
20490      * offset to be in a particular location (e.g., pass in 0,0 to set it
20491      * to the center of the object)
20492      * @method setDelta
20493      * @param {int} iDeltaX the distance from the left
20494      * @param {int} iDeltaY the distance from the top
20495      */
20496     setDelta: function(iDeltaX, iDeltaY) {
20497         this.deltaX = iDeltaX;
20498         this.deltaY = iDeltaY;
20499     },
20500
20501     /**
20502      * Sets the drag element to the location of the mousedown or click event,
20503      * maintaining the cursor location relative to the location on the element
20504      * that was clicked.  Override this if you want to place the element in a
20505      * location other than where the cursor is.
20506      * @method setDragElPos
20507      * @param {int} iPageX the X coordinate of the mousedown or drag event
20508      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20509      */
20510     setDragElPos: function(iPageX, iPageY) {
20511         // the first time we do this, we are going to check to make sure
20512         // the element has css positioning
20513
20514         var el = this.getDragEl();
20515         this.alignElWithMouse(el, iPageX, iPageY);
20516     },
20517
20518     /**
20519      * Sets the element to the location of the mousedown or click event,
20520      * maintaining the cursor location relative to the location on the element
20521      * that was clicked.  Override this if you want to place the element in a
20522      * location other than where the cursor is.
20523      * @method alignElWithMouse
20524      * @param {HTMLElement} el the element to move
20525      * @param {int} iPageX the X coordinate of the mousedown or drag event
20526      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20527      */
20528     alignElWithMouse: function(el, iPageX, iPageY) {
20529         var oCoord = this.getTargetCoord(iPageX, iPageY);
20530         var fly = el.dom ? el : Roo.fly(el);
20531         if (!this.deltaSetXY) {
20532             var aCoord = [oCoord.x, oCoord.y];
20533             fly.setXY(aCoord);
20534             var newLeft = fly.getLeft(true);
20535             var newTop  = fly.getTop(true);
20536             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20537         } else {
20538             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20539         }
20540
20541         this.cachePosition(oCoord.x, oCoord.y);
20542         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20543         return oCoord;
20544     },
20545
20546     /**
20547      * Saves the most recent position so that we can reset the constraints and
20548      * tick marks on-demand.  We need to know this so that we can calculate the
20549      * number of pixels the element is offset from its original position.
20550      * @method cachePosition
20551      * @param iPageX the current x position (optional, this just makes it so we
20552      * don't have to look it up again)
20553      * @param iPageY the current y position (optional, this just makes it so we
20554      * don't have to look it up again)
20555      */
20556     cachePosition: function(iPageX, iPageY) {
20557         if (iPageX) {
20558             this.lastPageX = iPageX;
20559             this.lastPageY = iPageY;
20560         } else {
20561             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20562             this.lastPageX = aCoord[0];
20563             this.lastPageY = aCoord[1];
20564         }
20565     },
20566
20567     /**
20568      * Auto-scroll the window if the dragged object has been moved beyond the
20569      * visible window boundary.
20570      * @method autoScroll
20571      * @param {int} x the drag element's x position
20572      * @param {int} y the drag element's y position
20573      * @param {int} h the height of the drag element
20574      * @param {int} w the width of the drag element
20575      * @private
20576      */
20577     autoScroll: function(x, y, h, w) {
20578
20579         if (this.scroll) {
20580             // The client height
20581             var clientH = Roo.lib.Dom.getViewWidth();
20582
20583             // The client width
20584             var clientW = Roo.lib.Dom.getViewHeight();
20585
20586             // The amt scrolled down
20587             var st = this.DDM.getScrollTop();
20588
20589             // The amt scrolled right
20590             var sl = this.DDM.getScrollLeft();
20591
20592             // Location of the bottom of the element
20593             var bot = h + y;
20594
20595             // Location of the right of the element
20596             var right = w + x;
20597
20598             // The distance from the cursor to the bottom of the visible area,
20599             // adjusted so that we don't scroll if the cursor is beyond the
20600             // element drag constraints
20601             var toBot = (clientH + st - y - this.deltaY);
20602
20603             // The distance from the cursor to the right of the visible area
20604             var toRight = (clientW + sl - x - this.deltaX);
20605
20606
20607             // How close to the edge the cursor must be before we scroll
20608             // var thresh = (document.all) ? 100 : 40;
20609             var thresh = 40;
20610
20611             // How many pixels to scroll per autoscroll op.  This helps to reduce
20612             // clunky scrolling. IE is more sensitive about this ... it needs this
20613             // value to be higher.
20614             var scrAmt = (document.all) ? 80 : 30;
20615
20616             // Scroll down if we are near the bottom of the visible page and the
20617             // obj extends below the crease
20618             if ( bot > clientH && toBot < thresh ) {
20619                 window.scrollTo(sl, st + scrAmt);
20620             }
20621
20622             // Scroll up if the window is scrolled down and the top of the object
20623             // goes above the top border
20624             if ( y < st && st > 0 && y - st < thresh ) {
20625                 window.scrollTo(sl, st - scrAmt);
20626             }
20627
20628             // Scroll right if the obj is beyond the right border and the cursor is
20629             // near the border.
20630             if ( right > clientW && toRight < thresh ) {
20631                 window.scrollTo(sl + scrAmt, st);
20632             }
20633
20634             // Scroll left if the window has been scrolled to the right and the obj
20635             // extends past the left border
20636             if ( x < sl && sl > 0 && x - sl < thresh ) {
20637                 window.scrollTo(sl - scrAmt, st);
20638             }
20639         }
20640     },
20641
20642     /**
20643      * Finds the location the element should be placed if we want to move
20644      * it to where the mouse location less the click offset would place us.
20645      * @method getTargetCoord
20646      * @param {int} iPageX the X coordinate of the click
20647      * @param {int} iPageY the Y coordinate of the click
20648      * @return an object that contains the coordinates (Object.x and Object.y)
20649      * @private
20650      */
20651     getTargetCoord: function(iPageX, iPageY) {
20652
20653
20654         var x = iPageX - this.deltaX;
20655         var y = iPageY - this.deltaY;
20656
20657         if (this.constrainX) {
20658             if (x < this.minX) { x = this.minX; }
20659             if (x > this.maxX) { x = this.maxX; }
20660         }
20661
20662         if (this.constrainY) {
20663             if (y < this.minY) { y = this.minY; }
20664             if (y > this.maxY) { y = this.maxY; }
20665         }
20666
20667         x = this.getTick(x, this.xTicks);
20668         y = this.getTick(y, this.yTicks);
20669
20670
20671         return {x:x, y:y};
20672     },
20673
20674     /*
20675      * Sets up config options specific to this class. Overrides
20676      * Roo.dd.DragDrop, but all versions of this method through the
20677      * inheritance chain are called
20678      */
20679     applyConfig: function() {
20680         Roo.dd.DD.superclass.applyConfig.call(this);
20681         this.scroll = (this.config.scroll !== false);
20682     },
20683
20684     /*
20685      * Event that fires prior to the onMouseDown event.  Overrides
20686      * Roo.dd.DragDrop.
20687      */
20688     b4MouseDown: function(e) {
20689         // this.resetConstraints();
20690         this.autoOffset(e.getPageX(),
20691                             e.getPageY());
20692     },
20693
20694     /*
20695      * Event that fires prior to the onDrag event.  Overrides
20696      * Roo.dd.DragDrop.
20697      */
20698     b4Drag: function(e) {
20699         this.setDragElPos(e.getPageX(),
20700                             e.getPageY());
20701     },
20702
20703     toString: function() {
20704         return ("DD " + this.id);
20705     }
20706
20707     //////////////////////////////////////////////////////////////////////////
20708     // Debugging ygDragDrop events that can be overridden
20709     //////////////////////////////////////////////////////////////////////////
20710     /*
20711     startDrag: function(x, y) {
20712     },
20713
20714     onDrag: function(e) {
20715     },
20716
20717     onDragEnter: function(e, id) {
20718     },
20719
20720     onDragOver: function(e, id) {
20721     },
20722
20723     onDragOut: function(e, id) {
20724     },
20725
20726     onDragDrop: function(e, id) {
20727     },
20728
20729     endDrag: function(e) {
20730     }
20731
20732     */
20733
20734 });/*
20735  * Based on:
20736  * Ext JS Library 1.1.1
20737  * Copyright(c) 2006-2007, Ext JS, LLC.
20738  *
20739  * Originally Released Under LGPL - original licence link has changed is not relivant.
20740  *
20741  * Fork - LGPL
20742  * <script type="text/javascript">
20743  */
20744
20745 /**
20746  * @class Roo.dd.DDProxy
20747  * A DragDrop implementation that inserts an empty, bordered div into
20748  * the document that follows the cursor during drag operations.  At the time of
20749  * the click, the frame div is resized to the dimensions of the linked html
20750  * element, and moved to the exact location of the linked element.
20751  *
20752  * References to the "frame" element refer to the single proxy element that
20753  * was created to be dragged in place of all DDProxy elements on the
20754  * page.
20755  *
20756  * @extends Roo.dd.DD
20757  * @constructor
20758  * @param {String} id the id of the linked html element
20759  * @param {String} sGroup the group of related DragDrop objects
20760  * @param {object} config an object containing configurable attributes
20761  *                Valid properties for DDProxy in addition to those in DragDrop:
20762  *                   resizeFrame, centerFrame, dragElId
20763  */
20764 Roo.dd.DDProxy = function(id, sGroup, config) {
20765     if (id) {
20766         this.init(id, sGroup, config);
20767         this.initFrame();
20768     }
20769 };
20770
20771 /**
20772  * The default drag frame div id
20773  * @property Roo.dd.DDProxy.dragElId
20774  * @type String
20775  * @static
20776  */
20777 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20778
20779 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20780
20781     /**
20782      * By default we resize the drag frame to be the same size as the element
20783      * we want to drag (this is to get the frame effect).  We can turn it off
20784      * if we want a different behavior.
20785      * @property resizeFrame
20786      * @type boolean
20787      */
20788     resizeFrame: true,
20789
20790     /**
20791      * By default the frame is positioned exactly where the drag element is, so
20792      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20793      * you do not have constraints on the obj is to have the drag frame centered
20794      * around the cursor.  Set centerFrame to true for this effect.
20795      * @property centerFrame
20796      * @type boolean
20797      */
20798     centerFrame: false,
20799
20800     /**
20801      * Creates the proxy element if it does not yet exist
20802      * @method createFrame
20803      */
20804     createFrame: function() {
20805         var self = this;
20806         var body = document.body;
20807
20808         if (!body || !body.firstChild) {
20809             setTimeout( function() { self.createFrame(); }, 50 );
20810             return;
20811         }
20812
20813         var div = this.getDragEl();
20814
20815         if (!div) {
20816             div    = document.createElement("div");
20817             div.id = this.dragElId;
20818             var s  = div.style;
20819
20820             s.position   = "absolute";
20821             s.visibility = "hidden";
20822             s.cursor     = "move";
20823             s.border     = "2px solid #aaa";
20824             s.zIndex     = 999;
20825
20826             // appendChild can blow up IE if invoked prior to the window load event
20827             // while rendering a table.  It is possible there are other scenarios
20828             // that would cause this to happen as well.
20829             body.insertBefore(div, body.firstChild);
20830         }
20831     },
20832
20833     /**
20834      * Initialization for the drag frame element.  Must be called in the
20835      * constructor of all subclasses
20836      * @method initFrame
20837      */
20838     initFrame: function() {
20839         this.createFrame();
20840     },
20841
20842     applyConfig: function() {
20843         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20844
20845         this.resizeFrame = (this.config.resizeFrame !== false);
20846         this.centerFrame = (this.config.centerFrame);
20847         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20848     },
20849
20850     /**
20851      * Resizes the drag frame to the dimensions of the clicked object, positions
20852      * it over the object, and finally displays it
20853      * @method showFrame
20854      * @param {int} iPageX X click position
20855      * @param {int} iPageY Y click position
20856      * @private
20857      */
20858     showFrame: function(iPageX, iPageY) {
20859         var el = this.getEl();
20860         var dragEl = this.getDragEl();
20861         var s = dragEl.style;
20862
20863         this._resizeProxy();
20864
20865         if (this.centerFrame) {
20866             this.setDelta( Math.round(parseInt(s.width,  10)/2),
20867                            Math.round(parseInt(s.height, 10)/2) );
20868         }
20869
20870         this.setDragElPos(iPageX, iPageY);
20871
20872         Roo.fly(dragEl).show();
20873     },
20874
20875     /**
20876      * The proxy is automatically resized to the dimensions of the linked
20877      * element when a drag is initiated, unless resizeFrame is set to false
20878      * @method _resizeProxy
20879      * @private
20880      */
20881     _resizeProxy: function() {
20882         if (this.resizeFrame) {
20883             var el = this.getEl();
20884             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
20885         }
20886     },
20887
20888     // overrides Roo.dd.DragDrop
20889     b4MouseDown: function(e) {
20890         var x = e.getPageX();
20891         var y = e.getPageY();
20892         this.autoOffset(x, y);
20893         this.setDragElPos(x, y);
20894     },
20895
20896     // overrides Roo.dd.DragDrop
20897     b4StartDrag: function(x, y) {
20898         // show the drag frame
20899         this.showFrame(x, y);
20900     },
20901
20902     // overrides Roo.dd.DragDrop
20903     b4EndDrag: function(e) {
20904         Roo.fly(this.getDragEl()).hide();
20905     },
20906
20907     // overrides Roo.dd.DragDrop
20908     // By default we try to move the element to the last location of the frame.
20909     // This is so that the default behavior mirrors that of Roo.dd.DD.
20910     endDrag: function(e) {
20911
20912         var lel = this.getEl();
20913         var del = this.getDragEl();
20914
20915         // Show the drag frame briefly so we can get its position
20916         del.style.visibility = "";
20917
20918         this.beforeMove();
20919         // Hide the linked element before the move to get around a Safari
20920         // rendering bug.
20921         lel.style.visibility = "hidden";
20922         Roo.dd.DDM.moveToEl(lel, del);
20923         del.style.visibility = "hidden";
20924         lel.style.visibility = "";
20925
20926         this.afterDrag();
20927     },
20928
20929     beforeMove : function(){
20930
20931     },
20932
20933     afterDrag : function(){
20934
20935     },
20936
20937     toString: function() {
20938         return ("DDProxy " + this.id);
20939     }
20940
20941 });
20942 /*
20943  * Based on:
20944  * Ext JS Library 1.1.1
20945  * Copyright(c) 2006-2007, Ext JS, LLC.
20946  *
20947  * Originally Released Under LGPL - original licence link has changed is not relivant.
20948  *
20949  * Fork - LGPL
20950  * <script type="text/javascript">
20951  */
20952
20953  /**
20954  * @class Roo.dd.DDTarget
20955  * A DragDrop implementation that does not move, but can be a drop
20956  * target.  You would get the same result by simply omitting implementation
20957  * for the event callbacks, but this way we reduce the processing cost of the
20958  * event listener and the callbacks.
20959  * @extends Roo.dd.DragDrop
20960  * @constructor
20961  * @param {String} id the id of the element that is a drop target
20962  * @param {String} sGroup the group of related DragDrop objects
20963  * @param {object} config an object containing configurable attributes
20964  *                 Valid properties for DDTarget in addition to those in
20965  *                 DragDrop:
20966  *                    none
20967  */
20968 Roo.dd.DDTarget = function(id, sGroup, config) {
20969     if (id) {
20970         this.initTarget(id, sGroup, config);
20971     }
20972     if (config.listeners || config.events) { 
20973        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
20974             listeners : config.listeners || {}, 
20975             events : config.events || {} 
20976         });    
20977     }
20978 };
20979
20980 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
20981 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
20982     toString: function() {
20983         return ("DDTarget " + this.id);
20984     }
20985 });
20986 /*
20987  * Based on:
20988  * Ext JS Library 1.1.1
20989  * Copyright(c) 2006-2007, Ext JS, LLC.
20990  *
20991  * Originally Released Under LGPL - original licence link has changed is not relivant.
20992  *
20993  * Fork - LGPL
20994  * <script type="text/javascript">
20995  */
20996  
20997
20998 /**
20999  * @class Roo.dd.ScrollManager
21000  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21001  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21002  * @singleton
21003  */
21004 Roo.dd.ScrollManager = function(){
21005     var ddm = Roo.dd.DragDropMgr;
21006     var els = {};
21007     var dragEl = null;
21008     var proc = {};
21009     
21010     
21011     
21012     var onStop = function(e){
21013         dragEl = null;
21014         clearProc();
21015     };
21016     
21017     var triggerRefresh = function(){
21018         if(ddm.dragCurrent){
21019              ddm.refreshCache(ddm.dragCurrent.groups);
21020         }
21021     };
21022     
21023     var doScroll = function(){
21024         if(ddm.dragCurrent){
21025             var dds = Roo.dd.ScrollManager;
21026             if(!dds.animate){
21027                 if(proc.el.scroll(proc.dir, dds.increment)){
21028                     triggerRefresh();
21029                 }
21030             }else{
21031                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21032             }
21033         }
21034     };
21035     
21036     var clearProc = function(){
21037         if(proc.id){
21038             clearInterval(proc.id);
21039         }
21040         proc.id = 0;
21041         proc.el = null;
21042         proc.dir = "";
21043     };
21044     
21045     var startProc = function(el, dir){
21046          Roo.log('scroll startproc');
21047         clearProc();
21048         proc.el = el;
21049         proc.dir = dir;
21050         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21051     };
21052     
21053     var onFire = function(e, isDrop){
21054        
21055         if(isDrop || !ddm.dragCurrent){ return; }
21056         var dds = Roo.dd.ScrollManager;
21057         if(!dragEl || dragEl != ddm.dragCurrent){
21058             dragEl = ddm.dragCurrent;
21059             // refresh regions on drag start
21060             dds.refreshCache();
21061         }
21062         
21063         var xy = Roo.lib.Event.getXY(e);
21064         var pt = new Roo.lib.Point(xy[0], xy[1]);
21065         for(var id in els){
21066             var el = els[id], r = el._region;
21067             if(r && r.contains(pt) && el.isScrollable()){
21068                 if(r.bottom - pt.y <= dds.thresh){
21069                     if(proc.el != el){
21070                         startProc(el, "down");
21071                     }
21072                     return;
21073                 }else if(r.right - pt.x <= dds.thresh){
21074                     if(proc.el != el){
21075                         startProc(el, "left");
21076                     }
21077                     return;
21078                 }else if(pt.y - r.top <= dds.thresh){
21079                     if(proc.el != el){
21080                         startProc(el, "up");
21081                     }
21082                     return;
21083                 }else if(pt.x - r.left <= dds.thresh){
21084                     if(proc.el != el){
21085                         startProc(el, "right");
21086                     }
21087                     return;
21088                 }
21089             }
21090         }
21091         clearProc();
21092     };
21093     
21094     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21095     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21096     
21097     return {
21098         /**
21099          * Registers new overflow element(s) to auto scroll
21100          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21101          */
21102         register : function(el){
21103             if(el instanceof Array){
21104                 for(var i = 0, len = el.length; i < len; i++) {
21105                         this.register(el[i]);
21106                 }
21107             }else{
21108                 el = Roo.get(el);
21109                 els[el.id] = el;
21110             }
21111             Roo.dd.ScrollManager.els = els;
21112         },
21113         
21114         /**
21115          * Unregisters overflow element(s) so they are no longer scrolled
21116          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21117          */
21118         unregister : function(el){
21119             if(el instanceof Array){
21120                 for(var i = 0, len = el.length; i < len; i++) {
21121                         this.unregister(el[i]);
21122                 }
21123             }else{
21124                 el = Roo.get(el);
21125                 delete els[el.id];
21126             }
21127         },
21128         
21129         /**
21130          * The number of pixels from the edge of a container the pointer needs to be to 
21131          * trigger scrolling (defaults to 25)
21132          * @type Number
21133          */
21134         thresh : 25,
21135         
21136         /**
21137          * The number of pixels to scroll in each scroll increment (defaults to 50)
21138          * @type Number
21139          */
21140         increment : 100,
21141         
21142         /**
21143          * The frequency of scrolls in milliseconds (defaults to 500)
21144          * @type Number
21145          */
21146         frequency : 500,
21147         
21148         /**
21149          * True to animate the scroll (defaults to true)
21150          * @type Boolean
21151          */
21152         animate: true,
21153         
21154         /**
21155          * The animation duration in seconds - 
21156          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21157          * @type Number
21158          */
21159         animDuration: .4,
21160         
21161         /**
21162          * Manually trigger a cache refresh.
21163          */
21164         refreshCache : function(){
21165             for(var id in els){
21166                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21167                     els[id]._region = els[id].getRegion();
21168                 }
21169             }
21170         }
21171     };
21172 }();/*
21173  * Based on:
21174  * Ext JS Library 1.1.1
21175  * Copyright(c) 2006-2007, Ext JS, LLC.
21176  *
21177  * Originally Released Under LGPL - original licence link has changed is not relivant.
21178  *
21179  * Fork - LGPL
21180  * <script type="text/javascript">
21181  */
21182  
21183
21184 /**
21185  * @class Roo.dd.Registry
21186  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21187  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21188  * @singleton
21189  */
21190 Roo.dd.Registry = function(){
21191     var elements = {}; 
21192     var handles = {}; 
21193     var autoIdSeed = 0;
21194
21195     var getId = function(el, autogen){
21196         if(typeof el == "string"){
21197             return el;
21198         }
21199         var id = el.id;
21200         if(!id && autogen !== false){
21201             id = "roodd-" + (++autoIdSeed);
21202             el.id = id;
21203         }
21204         return id;
21205     };
21206     
21207     return {
21208     /**
21209      * Register a drag drop element
21210      * @param {String|HTMLElement} element The id or DOM node to register
21211      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21212      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21213      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21214      * populated in the data object (if applicable):
21215      * <pre>
21216 Value      Description<br />
21217 ---------  ------------------------------------------<br />
21218 handles    Array of DOM nodes that trigger dragging<br />
21219            for the element being registered<br />
21220 isHandle   True if the element passed in triggers<br />
21221            dragging itself, else false
21222 </pre>
21223      */
21224         register : function(el, data){
21225             data = data || {};
21226             if(typeof el == "string"){
21227                 el = document.getElementById(el);
21228             }
21229             data.ddel = el;
21230             elements[getId(el)] = data;
21231             if(data.isHandle !== false){
21232                 handles[data.ddel.id] = data;
21233             }
21234             if(data.handles){
21235                 var hs = data.handles;
21236                 for(var i = 0, len = hs.length; i < len; i++){
21237                         handles[getId(hs[i])] = data;
21238                 }
21239             }
21240         },
21241
21242     /**
21243      * Unregister a drag drop element
21244      * @param {String|HTMLElement}  element The id or DOM node to unregister
21245      */
21246         unregister : function(el){
21247             var id = getId(el, false);
21248             var data = elements[id];
21249             if(data){
21250                 delete elements[id];
21251                 if(data.handles){
21252                     var hs = data.handles;
21253                     for(var i = 0, len = hs.length; i < len; i++){
21254                         delete handles[getId(hs[i], false)];
21255                     }
21256                 }
21257             }
21258         },
21259
21260     /**
21261      * Returns the handle registered for a DOM Node by id
21262      * @param {String|HTMLElement} id The DOM node or id to look up
21263      * @return {Object} handle The custom handle data
21264      */
21265         getHandle : function(id){
21266             if(typeof id != "string"){ // must be element?
21267                 id = id.id;
21268             }
21269             return handles[id];
21270         },
21271
21272     /**
21273      * Returns the handle that is registered for the DOM node that is the target of the event
21274      * @param {Event} e The event
21275      * @return {Object} handle The custom handle data
21276      */
21277         getHandleFromEvent : function(e){
21278             var t = Roo.lib.Event.getTarget(e);
21279             return t ? handles[t.id] : null;
21280         },
21281
21282     /**
21283      * Returns a custom data object that is registered for a DOM node by id
21284      * @param {String|HTMLElement} id The DOM node or id to look up
21285      * @return {Object} data The custom data
21286      */
21287         getTarget : function(id){
21288             if(typeof id != "string"){ // must be element?
21289                 id = id.id;
21290             }
21291             return elements[id];
21292         },
21293
21294     /**
21295      * Returns a custom data object that is registered for the DOM node that is the target of the event
21296      * @param {Event} e The event
21297      * @return {Object} data The custom data
21298      */
21299         getTargetFromEvent : function(e){
21300             var t = Roo.lib.Event.getTarget(e);
21301             return t ? elements[t.id] || handles[t.id] : null;
21302         }
21303     };
21304 }();/*
21305  * Based on:
21306  * Ext JS Library 1.1.1
21307  * Copyright(c) 2006-2007, Ext JS, LLC.
21308  *
21309  * Originally Released Under LGPL - original licence link has changed is not relivant.
21310  *
21311  * Fork - LGPL
21312  * <script type="text/javascript">
21313  */
21314  
21315
21316 /**
21317  * @class Roo.dd.StatusProxy
21318  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21319  * default drag proxy used by all Roo.dd components.
21320  * @constructor
21321  * @param {Object} config
21322  */
21323 Roo.dd.StatusProxy = function(config){
21324     Roo.apply(this, config);
21325     this.id = this.id || Roo.id();
21326     this.el = new Roo.Layer({
21327         dh: {
21328             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21329                 {tag: "div", cls: "x-dd-drop-icon"},
21330                 {tag: "div", cls: "x-dd-drag-ghost"}
21331             ]
21332         }, 
21333         shadow: !config || config.shadow !== false
21334     });
21335     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21336     this.dropStatus = this.dropNotAllowed;
21337 };
21338
21339 Roo.dd.StatusProxy.prototype = {
21340     /**
21341      * @cfg {String} dropAllowed
21342      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21343      */
21344     dropAllowed : "x-dd-drop-ok",
21345     /**
21346      * @cfg {String} dropNotAllowed
21347      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21348      */
21349     dropNotAllowed : "x-dd-drop-nodrop",
21350
21351     /**
21352      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21353      * over the current target element.
21354      * @param {String} cssClass The css class for the new drop status indicator image
21355      */
21356     setStatus : function(cssClass){
21357         cssClass = cssClass || this.dropNotAllowed;
21358         if(this.dropStatus != cssClass){
21359             this.el.replaceClass(this.dropStatus, cssClass);
21360             this.dropStatus = cssClass;
21361         }
21362     },
21363
21364     /**
21365      * Resets the status indicator to the default dropNotAllowed value
21366      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21367      */
21368     reset : function(clearGhost){
21369         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21370         this.dropStatus = this.dropNotAllowed;
21371         if(clearGhost){
21372             this.ghost.update("");
21373         }
21374     },
21375
21376     /**
21377      * Updates the contents of the ghost element
21378      * @param {String} html The html that will replace the current innerHTML of the ghost element
21379      */
21380     update : function(html){
21381         if(typeof html == "string"){
21382             this.ghost.update(html);
21383         }else{
21384             this.ghost.update("");
21385             html.style.margin = "0";
21386             this.ghost.dom.appendChild(html);
21387         }
21388         // ensure float = none set?? cant remember why though.
21389         var el = this.ghost.dom.firstChild;
21390                 if(el){
21391                         Roo.fly(el).setStyle('float', 'none');
21392                 }
21393     },
21394     
21395     /**
21396      * Returns the underlying proxy {@link Roo.Layer}
21397      * @return {Roo.Layer} el
21398     */
21399     getEl : function(){
21400         return this.el;
21401     },
21402
21403     /**
21404      * Returns the ghost element
21405      * @return {Roo.Element} el
21406      */
21407     getGhost : function(){
21408         return this.ghost;
21409     },
21410
21411     /**
21412      * Hides the proxy
21413      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21414      */
21415     hide : function(clear){
21416         this.el.hide();
21417         if(clear){
21418             this.reset(true);
21419         }
21420     },
21421
21422     /**
21423      * Stops the repair animation if it's currently running
21424      */
21425     stop : function(){
21426         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21427             this.anim.stop();
21428         }
21429     },
21430
21431     /**
21432      * Displays this proxy
21433      */
21434     show : function(){
21435         this.el.show();
21436     },
21437
21438     /**
21439      * Force the Layer to sync its shadow and shim positions to the element
21440      */
21441     sync : function(){
21442         this.el.sync();
21443     },
21444
21445     /**
21446      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21447      * invalid drop operation by the item being dragged.
21448      * @param {Array} xy The XY position of the element ([x, y])
21449      * @param {Function} callback The function to call after the repair is complete
21450      * @param {Object} scope The scope in which to execute the callback
21451      */
21452     repair : function(xy, callback, scope){
21453         this.callback = callback;
21454         this.scope = scope;
21455         if(xy && this.animRepair !== false){
21456             this.el.addClass("x-dd-drag-repair");
21457             this.el.hideUnders(true);
21458             this.anim = this.el.shift({
21459                 duration: this.repairDuration || .5,
21460                 easing: 'easeOut',
21461                 xy: xy,
21462                 stopFx: true,
21463                 callback: this.afterRepair,
21464                 scope: this
21465             });
21466         }else{
21467             this.afterRepair();
21468         }
21469     },
21470
21471     // private
21472     afterRepair : function(){
21473         this.hide(true);
21474         if(typeof this.callback == "function"){
21475             this.callback.call(this.scope || this);
21476         }
21477         this.callback = null;
21478         this.scope = null;
21479     }
21480 };/*
21481  * Based on:
21482  * Ext JS Library 1.1.1
21483  * Copyright(c) 2006-2007, Ext JS, LLC.
21484  *
21485  * Originally Released Under LGPL - original licence link has changed is not relivant.
21486  *
21487  * Fork - LGPL
21488  * <script type="text/javascript">
21489  */
21490
21491 /**
21492  * @class Roo.dd.DragSource
21493  * @extends Roo.dd.DDProxy
21494  * A simple class that provides the basic implementation needed to make any element draggable.
21495  * @constructor
21496  * @param {String/HTMLElement/Element} el The container element
21497  * @param {Object} config
21498  */
21499 Roo.dd.DragSource = function(el, config){
21500     this.el = Roo.get(el);
21501     this.dragData = {};
21502     
21503     Roo.apply(this, config);
21504     
21505     if(!this.proxy){
21506         this.proxy = new Roo.dd.StatusProxy();
21507     }
21508
21509     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21510           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21511     
21512     this.dragging = false;
21513 };
21514
21515 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21516     /**
21517      * @cfg {String} dropAllowed
21518      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21519      */
21520     dropAllowed : "x-dd-drop-ok",
21521     /**
21522      * @cfg {String} dropNotAllowed
21523      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21524      */
21525     dropNotAllowed : "x-dd-drop-nodrop",
21526
21527     /**
21528      * Returns the data object associated with this drag source
21529      * @return {Object} data An object containing arbitrary data
21530      */
21531     getDragData : function(e){
21532         return this.dragData;
21533     },
21534
21535     // private
21536     onDragEnter : function(e, id){
21537         var target = Roo.dd.DragDropMgr.getDDById(id);
21538         this.cachedTarget = target;
21539         if(this.beforeDragEnter(target, e, id) !== false){
21540             if(target.isNotifyTarget){
21541                 var status = target.notifyEnter(this, e, this.dragData);
21542                 this.proxy.setStatus(status);
21543             }else{
21544                 this.proxy.setStatus(this.dropAllowed);
21545             }
21546             
21547             if(this.afterDragEnter){
21548                 /**
21549                  * An empty function by default, but provided so that you can perform a custom action
21550                  * when the dragged item enters the drop target by providing an implementation.
21551                  * @param {Roo.dd.DragDrop} target The drop target
21552                  * @param {Event} e The event object
21553                  * @param {String} id The id of the dragged element
21554                  * @method afterDragEnter
21555                  */
21556                 this.afterDragEnter(target, e, id);
21557             }
21558         }
21559     },
21560
21561     /**
21562      * An empty function by default, but provided so that you can perform a custom action
21563      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21564      * @param {Roo.dd.DragDrop} target The drop target
21565      * @param {Event} e The event object
21566      * @param {String} id The id of the dragged element
21567      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21568      */
21569     beforeDragEnter : function(target, e, id){
21570         return true;
21571     },
21572
21573     // private
21574     alignElWithMouse: function() {
21575         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21576         this.proxy.sync();
21577     },
21578
21579     // private
21580     onDragOver : function(e, id){
21581         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21582         if(this.beforeDragOver(target, e, id) !== false){
21583             if(target.isNotifyTarget){
21584                 var status = target.notifyOver(this, e, this.dragData);
21585                 this.proxy.setStatus(status);
21586             }
21587
21588             if(this.afterDragOver){
21589                 /**
21590                  * An empty function by default, but provided so that you can perform a custom action
21591                  * while the dragged item is over the drop target by providing an implementation.
21592                  * @param {Roo.dd.DragDrop} target The drop target
21593                  * @param {Event} e The event object
21594                  * @param {String} id The id of the dragged element
21595                  * @method afterDragOver
21596                  */
21597                 this.afterDragOver(target, e, id);
21598             }
21599         }
21600     },
21601
21602     /**
21603      * An empty function by default, but provided so that you can perform a custom action
21604      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21605      * @param {Roo.dd.DragDrop} target The drop target
21606      * @param {Event} e The event object
21607      * @param {String} id The id of the dragged element
21608      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21609      */
21610     beforeDragOver : function(target, e, id){
21611         return true;
21612     },
21613
21614     // private
21615     onDragOut : function(e, id){
21616         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21617         if(this.beforeDragOut(target, e, id) !== false){
21618             if(target.isNotifyTarget){
21619                 target.notifyOut(this, e, this.dragData);
21620             }
21621             this.proxy.reset();
21622             if(this.afterDragOut){
21623                 /**
21624                  * An empty function by default, but provided so that you can perform a custom action
21625                  * after the dragged item is dragged out of the target without dropping.
21626                  * @param {Roo.dd.DragDrop} target The drop target
21627                  * @param {Event} e The event object
21628                  * @param {String} id The id of the dragged element
21629                  * @method afterDragOut
21630                  */
21631                 this.afterDragOut(target, e, id);
21632             }
21633         }
21634         this.cachedTarget = null;
21635     },
21636
21637     /**
21638      * An empty function by default, but provided so that you can perform a custom action before the dragged
21639      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21640      * @param {Roo.dd.DragDrop} target The drop target
21641      * @param {Event} e The event object
21642      * @param {String} id The id of the dragged element
21643      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21644      */
21645     beforeDragOut : function(target, e, id){
21646         return true;
21647     },
21648     
21649     // private
21650     onDragDrop : function(e, id){
21651         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21652         if(this.beforeDragDrop(target, e, id) !== false){
21653             if(target.isNotifyTarget){
21654                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21655                     this.onValidDrop(target, e, id);
21656                 }else{
21657                     this.onInvalidDrop(target, e, id);
21658                 }
21659             }else{
21660                 this.onValidDrop(target, e, id);
21661             }
21662             
21663             if(this.afterDragDrop){
21664                 /**
21665                  * An empty function by default, but provided so that you can perform a custom action
21666                  * after a valid drag drop has occurred by providing an implementation.
21667                  * @param {Roo.dd.DragDrop} target The drop target
21668                  * @param {Event} e The event object
21669                  * @param {String} id The id of the dropped element
21670                  * @method afterDragDrop
21671                  */
21672                 this.afterDragDrop(target, e, id);
21673             }
21674         }
21675         delete this.cachedTarget;
21676     },
21677
21678     /**
21679      * An empty function by default, but provided so that you can perform a custom action before the dragged
21680      * item is dropped onto the target and optionally cancel the onDragDrop.
21681      * @param {Roo.dd.DragDrop} target The drop target
21682      * @param {Event} e The event object
21683      * @param {String} id The id of the dragged element
21684      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21685      */
21686     beforeDragDrop : function(target, e, id){
21687         return true;
21688     },
21689
21690     // private
21691     onValidDrop : function(target, e, id){
21692         this.hideProxy();
21693         if(this.afterValidDrop){
21694             /**
21695              * An empty function by default, but provided so that you can perform a custom action
21696              * after a valid drop has occurred by providing an implementation.
21697              * @param {Object} target The target DD 
21698              * @param {Event} e The event object
21699              * @param {String} id The id of the dropped element
21700              * @method afterInvalidDrop
21701              */
21702             this.afterValidDrop(target, e, id);
21703         }
21704     },
21705
21706     // private
21707     getRepairXY : function(e, data){
21708         return this.el.getXY();  
21709     },
21710
21711     // private
21712     onInvalidDrop : function(target, e, id){
21713         this.beforeInvalidDrop(target, e, id);
21714         if(this.cachedTarget){
21715             if(this.cachedTarget.isNotifyTarget){
21716                 this.cachedTarget.notifyOut(this, e, this.dragData);
21717             }
21718             this.cacheTarget = null;
21719         }
21720         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21721
21722         if(this.afterInvalidDrop){
21723             /**
21724              * An empty function by default, but provided so that you can perform a custom action
21725              * after an invalid drop has occurred by providing an implementation.
21726              * @param {Event} e The event object
21727              * @param {String} id The id of the dropped element
21728              * @method afterInvalidDrop
21729              */
21730             this.afterInvalidDrop(e, id);
21731         }
21732     },
21733
21734     // private
21735     afterRepair : function(){
21736         if(Roo.enableFx){
21737             this.el.highlight(this.hlColor || "c3daf9");
21738         }
21739         this.dragging = false;
21740     },
21741
21742     /**
21743      * An empty function by default, but provided so that you can perform a custom action after an invalid
21744      * drop has occurred.
21745      * @param {Roo.dd.DragDrop} target The drop target
21746      * @param {Event} e The event object
21747      * @param {String} id The id of the dragged element
21748      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21749      */
21750     beforeInvalidDrop : function(target, e, id){
21751         return true;
21752     },
21753
21754     // private
21755     handleMouseDown : function(e){
21756         if(this.dragging) {
21757             return;
21758         }
21759         var data = this.getDragData(e);
21760         if(data && this.onBeforeDrag(data, e) !== false){
21761             this.dragData = data;
21762             this.proxy.stop();
21763             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21764         } 
21765     },
21766
21767     /**
21768      * An empty function by default, but provided so that you can perform a custom action before the initial
21769      * drag event begins and optionally cancel it.
21770      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21771      * @param {Event} e The event object
21772      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21773      */
21774     onBeforeDrag : function(data, e){
21775         return true;
21776     },
21777
21778     /**
21779      * An empty function by default, but provided so that you can perform a custom action once the initial
21780      * drag event has begun.  The drag cannot be canceled from this function.
21781      * @param {Number} x The x position of the click on the dragged object
21782      * @param {Number} y The y position of the click on the dragged object
21783      */
21784     onStartDrag : Roo.emptyFn,
21785
21786     // private - YUI override
21787     startDrag : function(x, y){
21788         this.proxy.reset();
21789         this.dragging = true;
21790         this.proxy.update("");
21791         this.onInitDrag(x, y);
21792         this.proxy.show();
21793     },
21794
21795     // private
21796     onInitDrag : function(x, y){
21797         var clone = this.el.dom.cloneNode(true);
21798         clone.id = Roo.id(); // prevent duplicate ids
21799         this.proxy.update(clone);
21800         this.onStartDrag(x, y);
21801         return true;
21802     },
21803
21804     /**
21805      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21806      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21807      */
21808     getProxy : function(){
21809         return this.proxy;  
21810     },
21811
21812     /**
21813      * Hides the drag source's {@link Roo.dd.StatusProxy}
21814      */
21815     hideProxy : function(){
21816         this.proxy.hide();  
21817         this.proxy.reset(true);
21818         this.dragging = false;
21819     },
21820
21821     // private
21822     triggerCacheRefresh : function(){
21823         Roo.dd.DDM.refreshCache(this.groups);
21824     },
21825
21826     // private - override to prevent hiding
21827     b4EndDrag: function(e) {
21828     },
21829
21830     // private - override to prevent moving
21831     endDrag : function(e){
21832         this.onEndDrag(this.dragData, e);
21833     },
21834
21835     // private
21836     onEndDrag : function(data, e){
21837     },
21838     
21839     // private - pin to cursor
21840     autoOffset : function(x, y) {
21841         this.setDelta(-12, -20);
21842     }    
21843 });/*
21844  * Based on:
21845  * Ext JS Library 1.1.1
21846  * Copyright(c) 2006-2007, Ext JS, LLC.
21847  *
21848  * Originally Released Under LGPL - original licence link has changed is not relivant.
21849  *
21850  * Fork - LGPL
21851  * <script type="text/javascript">
21852  */
21853
21854
21855 /**
21856  * @class Roo.dd.DropTarget
21857  * @extends Roo.dd.DDTarget
21858  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21859  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21860  * @constructor
21861  * @param {String/HTMLElement/Element} el The container element
21862  * @param {Object} config
21863  */
21864 Roo.dd.DropTarget = function(el, config){
21865     this.el = Roo.get(el);
21866     
21867     var listeners = false; ;
21868     if (config && config.listeners) {
21869         listeners= config.listeners;
21870         delete config.listeners;
21871     }
21872     Roo.apply(this, config);
21873     
21874     if(this.containerScroll){
21875         Roo.dd.ScrollManager.register(this.el);
21876     }
21877     this.addEvents( {
21878          /**
21879          * @scope Roo.dd.DropTarget
21880          */
21881          
21882          /**
21883          * @event enter
21884          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
21885          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
21886          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
21887          * 
21888          * IMPORTANT : it should set this.overClass and this.dropAllowed
21889          * 
21890          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21891          * @param {Event} e The event
21892          * @param {Object} data An object containing arbitrary data supplied by the drag source
21893          */
21894         "enter" : true,
21895         
21896          /**
21897          * @event over
21898          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
21899          * This method will be called on every mouse movement while the drag source is over the drop target.
21900          * This default implementation simply returns the dropAllowed config value.
21901          * 
21902          * IMPORTANT : it should set this.dropAllowed
21903          * 
21904          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21905          * @param {Event} e The event
21906          * @param {Object} data An object containing arbitrary data supplied by the drag source
21907          
21908          */
21909         "over" : true,
21910         /**
21911          * @event out
21912          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
21913          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
21914          * overClass (if any) from the drop element.
21915          * 
21916          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21917          * @param {Event} e The event
21918          * @param {Object} data An object containing arbitrary data supplied by the drag source
21919          */
21920          "out" : true,
21921          
21922         /**
21923          * @event drop
21924          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
21925          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
21926          * implementation that does something to process the drop event and returns true so that the drag source's
21927          * repair action does not run.
21928          * 
21929          * IMPORTANT : it should set this.success
21930          * 
21931          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
21932          * @param {Event} e The event
21933          * @param {Object} data An object containing arbitrary data supplied by the drag source
21934         */
21935          "drop" : true
21936     });
21937             
21938      
21939     Roo.dd.DropTarget.superclass.constructor.call(  this, 
21940         this.el.dom, 
21941         this.ddGroup || this.group,
21942         {
21943             isTarget: true,
21944             listeners : listeners || {} 
21945            
21946         
21947         }
21948     );
21949
21950 };
21951
21952 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
21953     /**
21954      * @cfg {String} overClass
21955      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
21956      */
21957      /**
21958      * @cfg {String} ddGroup
21959      * The drag drop group to handle drop events for
21960      */
21961      
21962     /**
21963      * @cfg {String} dropAllowed
21964      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21965      */
21966     dropAllowed : "x-dd-drop-ok",
21967     /**
21968      * @cfg {String} dropNotAllowed
21969      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21970      */
21971     dropNotAllowed : "x-dd-drop-nodrop",
21972     /**
21973      * @cfg {boolean} success
21974      * set this after drop listener.. 
21975      */
21976     success : false,
21977     /**
21978      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
21979      * if the drop point is valid for over/enter..
21980      */
21981     valid : false,
21982     // private
21983     isTarget : true,
21984
21985     // private
21986     isNotifyTarget : true,
21987     
21988     /**
21989      * @hide
21990      */
21991     notifyEnter : function(dd, e, data)
21992     {
21993         this.valid = true;
21994         this.fireEvent('enter', dd, e, data);
21995         if(this.overClass){
21996             this.el.addClass(this.overClass);
21997         }
21998         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
21999             this.valid ? this.dropAllowed : this.dropNotAllowed
22000         );
22001     },
22002
22003     /**
22004      * @hide
22005      */
22006     notifyOver : function(dd, e, data)
22007     {
22008         this.valid = true;
22009         this.fireEvent('over', dd, e, data);
22010         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22011             this.valid ? this.dropAllowed : this.dropNotAllowed
22012         );
22013     },
22014
22015     /**
22016      * @hide
22017      */
22018     notifyOut : function(dd, e, data)
22019     {
22020         this.fireEvent('out', dd, e, data);
22021         if(this.overClass){
22022             this.el.removeClass(this.overClass);
22023         }
22024     },
22025
22026     /**
22027      * @hide
22028      */
22029     notifyDrop : function(dd, e, data)
22030     {
22031         this.success = false;
22032         this.fireEvent('drop', dd, e, data);
22033         return this.success;
22034     }
22035 });/*
22036  * Based on:
22037  * Ext JS Library 1.1.1
22038  * Copyright(c) 2006-2007, Ext JS, LLC.
22039  *
22040  * Originally Released Under LGPL - original licence link has changed is not relivant.
22041  *
22042  * Fork - LGPL
22043  * <script type="text/javascript">
22044  */
22045
22046
22047 /**
22048  * @class Roo.dd.DragZone
22049  * @extends Roo.dd.DragSource
22050  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22051  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22052  * @constructor
22053  * @param {String/HTMLElement/Element} el The container element
22054  * @param {Object} config
22055  */
22056 Roo.dd.DragZone = function(el, config){
22057     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22058     if(this.containerScroll){
22059         Roo.dd.ScrollManager.register(this.el);
22060     }
22061 };
22062
22063 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22064     /**
22065      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22066      * for auto scrolling during drag operations.
22067      */
22068     /**
22069      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22070      * method after a failed drop (defaults to "c3daf9" - light blue)
22071      */
22072
22073     /**
22074      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22075      * for a valid target to drag based on the mouse down. Override this method
22076      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22077      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22078      * @param {EventObject} e The mouse down event
22079      * @return {Object} The dragData
22080      */
22081     getDragData : function(e){
22082         return Roo.dd.Registry.getHandleFromEvent(e);
22083     },
22084     
22085     /**
22086      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22087      * this.dragData.ddel
22088      * @param {Number} x The x position of the click on the dragged object
22089      * @param {Number} y The y position of the click on the dragged object
22090      * @return {Boolean} true to continue the drag, false to cancel
22091      */
22092     onInitDrag : function(x, y){
22093         this.proxy.update(this.dragData.ddel.cloneNode(true));
22094         this.onStartDrag(x, y);
22095         return true;
22096     },
22097     
22098     /**
22099      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22100      */
22101     afterRepair : function(){
22102         if(Roo.enableFx){
22103             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22104         }
22105         this.dragging = false;
22106     },
22107
22108     /**
22109      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22110      * the XY of this.dragData.ddel
22111      * @param {EventObject} e The mouse up event
22112      * @return {Array} The xy location (e.g. [100, 200])
22113      */
22114     getRepairXY : function(e){
22115         return Roo.Element.fly(this.dragData.ddel).getXY();  
22116     }
22117 });/*
22118  * Based on:
22119  * Ext JS Library 1.1.1
22120  * Copyright(c) 2006-2007, Ext JS, LLC.
22121  *
22122  * Originally Released Under LGPL - original licence link has changed is not relivant.
22123  *
22124  * Fork - LGPL
22125  * <script type="text/javascript">
22126  */
22127 /**
22128  * @class Roo.dd.DropZone
22129  * @extends Roo.dd.DropTarget
22130  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22131  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22132  * @constructor
22133  * @param {String/HTMLElement/Element} el The container element
22134  * @param {Object} config
22135  */
22136 Roo.dd.DropZone = function(el, config){
22137     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22138 };
22139
22140 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22141     /**
22142      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22143      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22144      * provide your own custom lookup.
22145      * @param {Event} e The event
22146      * @return {Object} data The custom data
22147      */
22148     getTargetFromEvent : function(e){
22149         return Roo.dd.Registry.getTargetFromEvent(e);
22150     },
22151
22152     /**
22153      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22154      * that it has registered.  This method has no default implementation and should be overridden to provide
22155      * node-specific processing if necessary.
22156      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22157      * {@link #getTargetFromEvent} for this node)
22158      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22159      * @param {Event} e The event
22160      * @param {Object} data An object containing arbitrary data supplied by the drag source
22161      */
22162     onNodeEnter : function(n, dd, e, data){
22163         
22164     },
22165
22166     /**
22167      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22168      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22169      * overridden to provide the proper feedback.
22170      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22171      * {@link #getTargetFromEvent} for this node)
22172      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22173      * @param {Event} e The event
22174      * @param {Object} data An object containing arbitrary data supplied by the drag source
22175      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22176      * underlying {@link Roo.dd.StatusProxy} can be updated
22177      */
22178     onNodeOver : function(n, dd, e, data){
22179         return this.dropAllowed;
22180     },
22181
22182     /**
22183      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22184      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22185      * node-specific processing if necessary.
22186      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22187      * {@link #getTargetFromEvent} for this node)
22188      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22189      * @param {Event} e The event
22190      * @param {Object} data An object containing arbitrary data supplied by the drag source
22191      */
22192     onNodeOut : function(n, dd, e, data){
22193         
22194     },
22195
22196     /**
22197      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22198      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22199      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22200      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22201      * {@link #getTargetFromEvent} for this node)
22202      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22203      * @param {Event} e The event
22204      * @param {Object} data An object containing arbitrary data supplied by the drag source
22205      * @return {Boolean} True if the drop was valid, else false
22206      */
22207     onNodeDrop : function(n, dd, e, data){
22208         return false;
22209     },
22210
22211     /**
22212      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22213      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22214      * it should be overridden to provide the proper feedback if necessary.
22215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22216      * @param {Event} e The event
22217      * @param {Object} data An object containing arbitrary data supplied by the drag source
22218      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22219      * underlying {@link Roo.dd.StatusProxy} can be updated
22220      */
22221     onContainerOver : function(dd, e, data){
22222         return this.dropNotAllowed;
22223     },
22224
22225     /**
22226      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22227      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22228      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22229      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22230      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22231      * @param {Event} e The event
22232      * @param {Object} data An object containing arbitrary data supplied by the drag source
22233      * @return {Boolean} True if the drop was valid, else false
22234      */
22235     onContainerDrop : function(dd, e, data){
22236         return false;
22237     },
22238
22239     /**
22240      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22241      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22242      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22243      * you should override this method and provide a custom implementation.
22244      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22245      * @param {Event} e The event
22246      * @param {Object} data An object containing arbitrary data supplied by the drag source
22247      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22248      * underlying {@link Roo.dd.StatusProxy} can be updated
22249      */
22250     notifyEnter : function(dd, e, data){
22251         return this.dropNotAllowed;
22252     },
22253
22254     /**
22255      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22256      * This method will be called on every mouse movement while the drag source is over the drop zone.
22257      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22258      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22259      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22260      * registered node, it will call {@link #onContainerOver}.
22261      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22262      * @param {Event} e The event
22263      * @param {Object} data An object containing arbitrary data supplied by the drag source
22264      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22265      * underlying {@link Roo.dd.StatusProxy} can be updated
22266      */
22267     notifyOver : function(dd, e, data){
22268         var n = this.getTargetFromEvent(e);
22269         if(!n){ // not over valid drop target
22270             if(this.lastOverNode){
22271                 this.onNodeOut(this.lastOverNode, dd, e, data);
22272                 this.lastOverNode = null;
22273             }
22274             return this.onContainerOver(dd, e, data);
22275         }
22276         if(this.lastOverNode != n){
22277             if(this.lastOverNode){
22278                 this.onNodeOut(this.lastOverNode, dd, e, data);
22279             }
22280             this.onNodeEnter(n, dd, e, data);
22281             this.lastOverNode = n;
22282         }
22283         return this.onNodeOver(n, dd, e, data);
22284     },
22285
22286     /**
22287      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22288      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22289      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22290      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22291      * @param {Event} e The event
22292      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22293      */
22294     notifyOut : function(dd, e, data){
22295         if(this.lastOverNode){
22296             this.onNodeOut(this.lastOverNode, dd, e, data);
22297             this.lastOverNode = null;
22298         }
22299     },
22300
22301     /**
22302      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22303      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22304      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22305      * otherwise it will call {@link #onContainerDrop}.
22306      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22307      * @param {Event} e The event
22308      * @param {Object} data An object containing arbitrary data supplied by the drag source
22309      * @return {Boolean} True if the drop was valid, else false
22310      */
22311     notifyDrop : function(dd, e, data){
22312         if(this.lastOverNode){
22313             this.onNodeOut(this.lastOverNode, dd, e, data);
22314             this.lastOverNode = null;
22315         }
22316         var n = this.getTargetFromEvent(e);
22317         return n ?
22318             this.onNodeDrop(n, dd, e, data) :
22319             this.onContainerDrop(dd, e, data);
22320     },
22321
22322     // private
22323     triggerCacheRefresh : function(){
22324         Roo.dd.DDM.refreshCache(this.groups);
22325     }  
22326 });/*
22327  * Based on:
22328  * Ext JS Library 1.1.1
22329  * Copyright(c) 2006-2007, Ext JS, LLC.
22330  *
22331  * Originally Released Under LGPL - original licence link has changed is not relivant.
22332  *
22333  * Fork - LGPL
22334  * <script type="text/javascript">
22335  */
22336
22337
22338 /**
22339  * @class Roo.data.SortTypes
22340  * @singleton
22341  * Defines the default sorting (casting?) comparison functions used when sorting data.
22342  */
22343 Roo.data.SortTypes = {
22344     /**
22345      * Default sort that does nothing
22346      * @param {Mixed} s The value being converted
22347      * @return {Mixed} The comparison value
22348      */
22349     none : function(s){
22350         return s;
22351     },
22352     
22353     /**
22354      * The regular expression used to strip tags
22355      * @type {RegExp}
22356      * @property
22357      */
22358     stripTagsRE : /<\/?[^>]+>/gi,
22359     
22360     /**
22361      * Strips all HTML tags to sort on text only
22362      * @param {Mixed} s The value being converted
22363      * @return {String} The comparison value
22364      */
22365     asText : function(s){
22366         return String(s).replace(this.stripTagsRE, "");
22367     },
22368     
22369     /**
22370      * Strips all HTML tags to sort on text only - Case insensitive
22371      * @param {Mixed} s The value being converted
22372      * @return {String} The comparison value
22373      */
22374     asUCText : function(s){
22375         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22376     },
22377     
22378     /**
22379      * Case insensitive string
22380      * @param {Mixed} s The value being converted
22381      * @return {String} The comparison value
22382      */
22383     asUCString : function(s) {
22384         return String(s).toUpperCase();
22385     },
22386     
22387     /**
22388      * Date sorting
22389      * @param {Mixed} s The value being converted
22390      * @return {Number} The comparison value
22391      */
22392     asDate : function(s) {
22393         if(!s){
22394             return 0;
22395         }
22396         if(s instanceof Date){
22397             return s.getTime();
22398         }
22399         return Date.parse(String(s));
22400     },
22401     
22402     /**
22403      * Float sorting
22404      * @param {Mixed} s The value being converted
22405      * @return {Float} The comparison value
22406      */
22407     asFloat : function(s) {
22408         var val = parseFloat(String(s).replace(/,/g, ""));
22409         if(isNaN(val)) {
22410             val = 0;
22411         }
22412         return val;
22413     },
22414     
22415     /**
22416      * Integer sorting
22417      * @param {Mixed} s The value being converted
22418      * @return {Number} The comparison value
22419      */
22420     asInt : function(s) {
22421         var val = parseInt(String(s).replace(/,/g, ""));
22422         if(isNaN(val)) {
22423             val = 0;
22424         }
22425         return val;
22426     }
22427 };/*
22428  * Based on:
22429  * Ext JS Library 1.1.1
22430  * Copyright(c) 2006-2007, Ext JS, LLC.
22431  *
22432  * Originally Released Under LGPL - original licence link has changed is not relivant.
22433  *
22434  * Fork - LGPL
22435  * <script type="text/javascript">
22436  */
22437
22438 /**
22439 * @class Roo.data.Record
22440  * Instances of this class encapsulate both record <em>definition</em> information, and record
22441  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22442  * to access Records cached in an {@link Roo.data.Store} object.<br>
22443  * <p>
22444  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22445  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22446  * objects.<br>
22447  * <p>
22448  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22449  * @constructor
22450  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22451  * {@link #create}. The parameters are the same.
22452  * @param {Array} data An associative Array of data values keyed by the field name.
22453  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22454  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22455  * not specified an integer id is generated.
22456  */
22457 Roo.data.Record = function(data, id){
22458     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22459     this.data = data;
22460 };
22461
22462 /**
22463  * Generate a constructor for a specific record layout.
22464  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22465  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22466  * Each field definition object may contain the following properties: <ul>
22467  * <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,
22468  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22469  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22470  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22471  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22472  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22473  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22474  * this may be omitted.</p></li>
22475  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22476  * <ul><li>auto (Default, implies no conversion)</li>
22477  * <li>string</li>
22478  * <li>int</li>
22479  * <li>float</li>
22480  * <li>boolean</li>
22481  * <li>date</li></ul></p></li>
22482  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22483  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22484  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22485  * by the Reader into an object that will be stored in the Record. It is passed the
22486  * following parameters:<ul>
22487  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22488  * </ul></p></li>
22489  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22490  * </ul>
22491  * <br>usage:<br><pre><code>
22492 var TopicRecord = Roo.data.Record.create(
22493     {name: 'title', mapping: 'topic_title'},
22494     {name: 'author', mapping: 'username'},
22495     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22496     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22497     {name: 'lastPoster', mapping: 'user2'},
22498     {name: 'excerpt', mapping: 'post_text'}
22499 );
22500
22501 var myNewRecord = new TopicRecord({
22502     title: 'Do my job please',
22503     author: 'noobie',
22504     totalPosts: 1,
22505     lastPost: new Date(),
22506     lastPoster: 'Animal',
22507     excerpt: 'No way dude!'
22508 });
22509 myStore.add(myNewRecord);
22510 </code></pre>
22511  * @method create
22512  * @static
22513  */
22514 Roo.data.Record.create = function(o){
22515     var f = function(){
22516         f.superclass.constructor.apply(this, arguments);
22517     };
22518     Roo.extend(f, Roo.data.Record);
22519     var p = f.prototype;
22520     p.fields = new Roo.util.MixedCollection(false, function(field){
22521         return field.name;
22522     });
22523     for(var i = 0, len = o.length; i < len; i++){
22524         p.fields.add(new Roo.data.Field(o[i]));
22525     }
22526     f.getField = function(name){
22527         return p.fields.get(name);  
22528     };
22529     return f;
22530 };
22531
22532 Roo.data.Record.AUTO_ID = 1000;
22533 Roo.data.Record.EDIT = 'edit';
22534 Roo.data.Record.REJECT = 'reject';
22535 Roo.data.Record.COMMIT = 'commit';
22536
22537 Roo.data.Record.prototype = {
22538     /**
22539      * Readonly flag - true if this record has been modified.
22540      * @type Boolean
22541      */
22542     dirty : false,
22543     editing : false,
22544     error: null,
22545     modified: null,
22546
22547     // private
22548     join : function(store){
22549         this.store = store;
22550     },
22551
22552     /**
22553      * Set the named field to the specified value.
22554      * @param {String} name The name of the field to set.
22555      * @param {Object} value The value to set the field to.
22556      */
22557     set : function(name, value){
22558         if(this.data[name] == value){
22559             return;
22560         }
22561         this.dirty = true;
22562         if(!this.modified){
22563             this.modified = {};
22564         }
22565         if(typeof this.modified[name] == 'undefined'){
22566             this.modified[name] = this.data[name];
22567         }
22568         this.data[name] = value;
22569         if(!this.editing && this.store){
22570             this.store.afterEdit(this);
22571         }       
22572     },
22573
22574     /**
22575      * Get the value of the named field.
22576      * @param {String} name The name of the field to get the value of.
22577      * @return {Object} The value of the field.
22578      */
22579     get : function(name){
22580         return this.data[name]; 
22581     },
22582
22583     // private
22584     beginEdit : function(){
22585         this.editing = true;
22586         this.modified = {}; 
22587     },
22588
22589     // private
22590     cancelEdit : function(){
22591         this.editing = false;
22592         delete this.modified;
22593     },
22594
22595     // private
22596     endEdit : function(){
22597         this.editing = false;
22598         if(this.dirty && this.store){
22599             this.store.afterEdit(this);
22600         }
22601     },
22602
22603     /**
22604      * Usually called by the {@link Roo.data.Store} which owns the Record.
22605      * Rejects all changes made to the Record since either creation, or the last commit operation.
22606      * Modified fields are reverted to their original values.
22607      * <p>
22608      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22609      * of reject operations.
22610      */
22611     reject : function(){
22612         var m = this.modified;
22613         for(var n in m){
22614             if(typeof m[n] != "function"){
22615                 this.data[n] = m[n];
22616             }
22617         }
22618         this.dirty = false;
22619         delete this.modified;
22620         this.editing = false;
22621         if(this.store){
22622             this.store.afterReject(this);
22623         }
22624     },
22625
22626     /**
22627      * Usually called by the {@link Roo.data.Store} which owns the Record.
22628      * Commits all changes made to the Record since either creation, or the last commit operation.
22629      * <p>
22630      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22631      * of commit operations.
22632      */
22633     commit : function(){
22634         this.dirty = false;
22635         delete this.modified;
22636         this.editing = false;
22637         if(this.store){
22638             this.store.afterCommit(this);
22639         }
22640     },
22641
22642     // private
22643     hasError : function(){
22644         return this.error != null;
22645     },
22646
22647     // private
22648     clearError : function(){
22649         this.error = null;
22650     },
22651
22652     /**
22653      * Creates a copy of this record.
22654      * @param {String} id (optional) A new record id if you don't want to use this record's id
22655      * @return {Record}
22656      */
22657     copy : function(newId) {
22658         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22659     }
22660 };/*
22661  * Based on:
22662  * Ext JS Library 1.1.1
22663  * Copyright(c) 2006-2007, Ext JS, LLC.
22664  *
22665  * Originally Released Under LGPL - original licence link has changed is not relivant.
22666  *
22667  * Fork - LGPL
22668  * <script type="text/javascript">
22669  */
22670
22671
22672
22673 /**
22674  * @class Roo.data.Store
22675  * @extends Roo.util.Observable
22676  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22677  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22678  * <p>
22679  * 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
22680  * has no knowledge of the format of the data returned by the Proxy.<br>
22681  * <p>
22682  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22683  * instances from the data object. These records are cached and made available through accessor functions.
22684  * @constructor
22685  * Creates a new Store.
22686  * @param {Object} config A config object containing the objects needed for the Store to access data,
22687  * and read the data into Records.
22688  */
22689 Roo.data.Store = function(config){
22690     this.data = new Roo.util.MixedCollection(false);
22691     this.data.getKey = function(o){
22692         return o.id;
22693     };
22694     this.baseParams = {};
22695     // private
22696     this.paramNames = {
22697         "start" : "start",
22698         "limit" : "limit",
22699         "sort" : "sort",
22700         "dir" : "dir",
22701         "multisort" : "_multisort"
22702     };
22703
22704     if(config && config.data){
22705         this.inlineData = config.data;
22706         delete config.data;
22707     }
22708
22709     Roo.apply(this, config);
22710     
22711     if(this.reader){ // reader passed
22712         this.reader = Roo.factory(this.reader, Roo.data);
22713         this.reader.xmodule = this.xmodule || false;
22714         if(!this.recordType){
22715             this.recordType = this.reader.recordType;
22716         }
22717         if(this.reader.onMetaChange){
22718             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22719         }
22720     }
22721
22722     if(this.recordType){
22723         this.fields = this.recordType.prototype.fields;
22724     }
22725     this.modified = [];
22726
22727     this.addEvents({
22728         /**
22729          * @event datachanged
22730          * Fires when the data cache has changed, and a widget which is using this Store
22731          * as a Record cache should refresh its view.
22732          * @param {Store} this
22733          */
22734         datachanged : true,
22735         /**
22736          * @event metachange
22737          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22738          * @param {Store} this
22739          * @param {Object} meta The JSON metadata
22740          */
22741         metachange : true,
22742         /**
22743          * @event add
22744          * Fires when Records have been added to the Store
22745          * @param {Store} this
22746          * @param {Roo.data.Record[]} records The array of Records added
22747          * @param {Number} index The index at which the record(s) were added
22748          */
22749         add : true,
22750         /**
22751          * @event remove
22752          * Fires when a Record has been removed from the Store
22753          * @param {Store} this
22754          * @param {Roo.data.Record} record The Record that was removed
22755          * @param {Number} index The index at which the record was removed
22756          */
22757         remove : true,
22758         /**
22759          * @event update
22760          * Fires when a Record has been updated
22761          * @param {Store} this
22762          * @param {Roo.data.Record} record The Record that was updated
22763          * @param {String} operation The update operation being performed.  Value may be one of:
22764          * <pre><code>
22765  Roo.data.Record.EDIT
22766  Roo.data.Record.REJECT
22767  Roo.data.Record.COMMIT
22768          * </code></pre>
22769          */
22770         update : true,
22771         /**
22772          * @event clear
22773          * Fires when the data cache has been cleared.
22774          * @param {Store} this
22775          */
22776         clear : true,
22777         /**
22778          * @event beforeload
22779          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22780          * the load action will be canceled.
22781          * @param {Store} this
22782          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22783          */
22784         beforeload : true,
22785         /**
22786          * @event beforeloadadd
22787          * Fires after a new set of Records has been loaded.
22788          * @param {Store} this
22789          * @param {Roo.data.Record[]} records The Records that were loaded
22790          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22791          */
22792         beforeloadadd : true,
22793         /**
22794          * @event load
22795          * Fires after a new set of Records has been loaded, before they are added to the store.
22796          * @param {Store} this
22797          * @param {Roo.data.Record[]} records The Records that were loaded
22798          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22799          * @params {Object} return from reader
22800          */
22801         load : true,
22802         /**
22803          * @event loadexception
22804          * Fires if an exception occurs in the Proxy during loading.
22805          * Called with the signature of the Proxy's "loadexception" event.
22806          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22807          * 
22808          * @param {Proxy} 
22809          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22810          * @param {Object} load options 
22811          * @param {Object} jsonData from your request (normally this contains the Exception)
22812          */
22813         loadexception : true
22814     });
22815     
22816     if(this.proxy){
22817         this.proxy = Roo.factory(this.proxy, Roo.data);
22818         this.proxy.xmodule = this.xmodule || false;
22819         this.relayEvents(this.proxy,  ["loadexception"]);
22820     }
22821     this.sortToggle = {};
22822     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22823
22824     Roo.data.Store.superclass.constructor.call(this);
22825
22826     if(this.inlineData){
22827         this.loadData(this.inlineData);
22828         delete this.inlineData;
22829     }
22830 };
22831
22832 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22833      /**
22834     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22835     * without a remote query - used by combo/forms at present.
22836     */
22837     
22838     /**
22839     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22840     */
22841     /**
22842     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22843     */
22844     /**
22845     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22846     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22847     */
22848     /**
22849     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22850     * on any HTTP request
22851     */
22852     /**
22853     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22854     */
22855     /**
22856     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22857     */
22858     multiSort: false,
22859     /**
22860     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22861     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22862     */
22863     remoteSort : false,
22864
22865     /**
22866     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
22867      * loaded or when a record is removed. (defaults to false).
22868     */
22869     pruneModifiedRecords : false,
22870
22871     // private
22872     lastOptions : null,
22873
22874     /**
22875      * Add Records to the Store and fires the add event.
22876      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22877      */
22878     add : function(records){
22879         records = [].concat(records);
22880         for(var i = 0, len = records.length; i < len; i++){
22881             records[i].join(this);
22882         }
22883         var index = this.data.length;
22884         this.data.addAll(records);
22885         this.fireEvent("add", this, records, index);
22886     },
22887
22888     /**
22889      * Remove a Record from the Store and fires the remove event.
22890      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
22891      */
22892     remove : function(record){
22893         var index = this.data.indexOf(record);
22894         this.data.removeAt(index);
22895         if(this.pruneModifiedRecords){
22896             this.modified.remove(record);
22897         }
22898         this.fireEvent("remove", this, record, index);
22899     },
22900
22901     /**
22902      * Remove all Records from the Store and fires the clear event.
22903      */
22904     removeAll : function(){
22905         this.data.clear();
22906         if(this.pruneModifiedRecords){
22907             this.modified = [];
22908         }
22909         this.fireEvent("clear", this);
22910     },
22911
22912     /**
22913      * Inserts Records to the Store at the given index and fires the add event.
22914      * @param {Number} index The start index at which to insert the passed Records.
22915      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
22916      */
22917     insert : function(index, records){
22918         records = [].concat(records);
22919         for(var i = 0, len = records.length; i < len; i++){
22920             this.data.insert(index, records[i]);
22921             records[i].join(this);
22922         }
22923         this.fireEvent("add", this, records, index);
22924     },
22925
22926     /**
22927      * Get the index within the cache of the passed Record.
22928      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
22929      * @return {Number} The index of the passed Record. Returns -1 if not found.
22930      */
22931     indexOf : function(record){
22932         return this.data.indexOf(record);
22933     },
22934
22935     /**
22936      * Get the index within the cache of the Record with the passed id.
22937      * @param {String} id The id of the Record to find.
22938      * @return {Number} The index of the Record. Returns -1 if not found.
22939      */
22940     indexOfId : function(id){
22941         return this.data.indexOfKey(id);
22942     },
22943
22944     /**
22945      * Get the Record with the specified id.
22946      * @param {String} id The id of the Record to find.
22947      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
22948      */
22949     getById : function(id){
22950         return this.data.key(id);
22951     },
22952
22953     /**
22954      * Get the Record at the specified index.
22955      * @param {Number} index The index of the Record to find.
22956      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
22957      */
22958     getAt : function(index){
22959         return this.data.itemAt(index);
22960     },
22961
22962     /**
22963      * Returns a range of Records between specified indices.
22964      * @param {Number} startIndex (optional) The starting index (defaults to 0)
22965      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
22966      * @return {Roo.data.Record[]} An array of Records
22967      */
22968     getRange : function(start, end){
22969         return this.data.getRange(start, end);
22970     },
22971
22972     // private
22973     storeOptions : function(o){
22974         o = Roo.apply({}, o);
22975         delete o.callback;
22976         delete o.scope;
22977         this.lastOptions = o;
22978     },
22979
22980     /**
22981      * Loads the Record cache from the configured Proxy using the configured Reader.
22982      * <p>
22983      * If using remote paging, then the first load call must specify the <em>start</em>
22984      * and <em>limit</em> properties in the options.params property to establish the initial
22985      * position within the dataset, and the number of Records to cache on each read from the Proxy.
22986      * <p>
22987      * <strong>It is important to note that for remote data sources, loading is asynchronous,
22988      * and this call will return before the new data has been loaded. Perform any post-processing
22989      * in a callback function, or in a "load" event handler.</strong>
22990      * <p>
22991      * @param {Object} options An object containing properties which control loading options:<ul>
22992      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
22993      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
22994      * passed the following arguments:<ul>
22995      * <li>r : Roo.data.Record[]</li>
22996      * <li>options: Options object from the load call</li>
22997      * <li>success: Boolean success indicator</li></ul></li>
22998      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
22999      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23000      * </ul>
23001      */
23002     load : function(options){
23003         options = options || {};
23004         if(this.fireEvent("beforeload", this, options) !== false){
23005             this.storeOptions(options);
23006             var p = Roo.apply(options.params || {}, this.baseParams);
23007             // if meta was not loaded from remote source.. try requesting it.
23008             if (!this.reader.metaFromRemote) {
23009                 p._requestMeta = 1;
23010             }
23011             if(this.sortInfo && this.remoteSort){
23012                 var pn = this.paramNames;
23013                 p[pn["sort"]] = this.sortInfo.field;
23014                 p[pn["dir"]] = this.sortInfo.direction;
23015             }
23016             if (this.multiSort) {
23017                 var pn = this.paramNames;
23018                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23019             }
23020             
23021             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23022         }
23023     },
23024
23025     /**
23026      * Reloads the Record cache from the configured Proxy using the configured Reader and
23027      * the options from the last load operation performed.
23028      * @param {Object} options (optional) An object containing properties which may override the options
23029      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23030      * the most recently used options are reused).
23031      */
23032     reload : function(options){
23033         this.load(Roo.applyIf(options||{}, this.lastOptions));
23034     },
23035
23036     // private
23037     // Called as a callback by the Reader during a load operation.
23038     loadRecords : function(o, options, success){
23039         if(!o || success === false){
23040             if(success !== false){
23041                 this.fireEvent("load", this, [], options, o);
23042             }
23043             if(options.callback){
23044                 options.callback.call(options.scope || this, [], options, false);
23045             }
23046             return;
23047         }
23048         // if data returned failure - throw an exception.
23049         if (o.success === false) {
23050             // show a message if no listener is registered.
23051             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23052                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23053             }
23054             // loadmask wil be hooked into this..
23055             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23056             return;
23057         }
23058         var r = o.records, t = o.totalRecords || r.length;
23059         
23060         this.fireEvent("beforeloadadd", this, r, options, o);
23061         
23062         if(!options || options.add !== true){
23063             if(this.pruneModifiedRecords){
23064                 this.modified = [];
23065             }
23066             for(var i = 0, len = r.length; i < len; i++){
23067                 r[i].join(this);
23068             }
23069             if(this.snapshot){
23070                 this.data = this.snapshot;
23071                 delete this.snapshot;
23072             }
23073             this.data.clear();
23074             this.data.addAll(r);
23075             this.totalLength = t;
23076             this.applySort();
23077             this.fireEvent("datachanged", this);
23078         }else{
23079             this.totalLength = Math.max(t, this.data.length+r.length);
23080             this.add(r);
23081         }
23082         
23083         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23084                 
23085             var e = new Roo.data.Record({});
23086
23087             e.set(this.parent.displayField, this.parent.emptyTitle);
23088             e.set(this.parent.valueField, '');
23089
23090             this.insert(0, e);
23091         }
23092             
23093         this.fireEvent("load", this, r, options, o);
23094         if(options.callback){
23095             options.callback.call(options.scope || this, r, options, true);
23096         }
23097     },
23098
23099
23100     /**
23101      * Loads data from a passed data block. A Reader which understands the format of the data
23102      * must have been configured in the constructor.
23103      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23104      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23105      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23106      */
23107     loadData : function(o, append){
23108         var r = this.reader.readRecords(o);
23109         this.loadRecords(r, {add: append}, true);
23110     },
23111
23112     /**
23113      * Gets the number of cached records.
23114      * <p>
23115      * <em>If using paging, this may not be the total size of the dataset. If the data object
23116      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23117      * the data set size</em>
23118      */
23119     getCount : function(){
23120         return this.data.length || 0;
23121     },
23122
23123     /**
23124      * Gets the total number of records in the dataset as returned by the server.
23125      * <p>
23126      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23127      * the dataset size</em>
23128      */
23129     getTotalCount : function(){
23130         return this.totalLength || 0;
23131     },
23132
23133     /**
23134      * Returns the sort state of the Store as an object with two properties:
23135      * <pre><code>
23136  field {String} The name of the field by which the Records are sorted
23137  direction {String} The sort order, "ASC" or "DESC"
23138      * </code></pre>
23139      */
23140     getSortState : function(){
23141         return this.sortInfo;
23142     },
23143
23144     // private
23145     applySort : function(){
23146         if(this.sortInfo && !this.remoteSort){
23147             var s = this.sortInfo, f = s.field;
23148             var st = this.fields.get(f).sortType;
23149             var fn = function(r1, r2){
23150                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23151                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23152             };
23153             this.data.sort(s.direction, fn);
23154             if(this.snapshot && this.snapshot != this.data){
23155                 this.snapshot.sort(s.direction, fn);
23156             }
23157         }
23158     },
23159
23160     /**
23161      * Sets the default sort column and order to be used by the next load operation.
23162      * @param {String} fieldName The name of the field to sort by.
23163      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23164      */
23165     setDefaultSort : function(field, dir){
23166         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23167     },
23168
23169     /**
23170      * Sort the Records.
23171      * If remote sorting is used, the sort is performed on the server, and the cache is
23172      * reloaded. If local sorting is used, the cache is sorted internally.
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     sort : function(fieldName, dir){
23177         var f = this.fields.get(fieldName);
23178         if(!dir){
23179             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23180             
23181             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23182                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23183             }else{
23184                 dir = f.sortDir;
23185             }
23186         }
23187         this.sortToggle[f.name] = dir;
23188         this.sortInfo = {field: f.name, direction: dir};
23189         if(!this.remoteSort){
23190             this.applySort();
23191             this.fireEvent("datachanged", this);
23192         }else{
23193             this.load(this.lastOptions);
23194         }
23195     },
23196
23197     /**
23198      * Calls the specified function for each of the Records in the cache.
23199      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23200      * Returning <em>false</em> aborts and exits the iteration.
23201      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23202      */
23203     each : function(fn, scope){
23204         this.data.each(fn, scope);
23205     },
23206
23207     /**
23208      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23209      * (e.g., during paging).
23210      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23211      */
23212     getModifiedRecords : function(){
23213         return this.modified;
23214     },
23215
23216     // private
23217     createFilterFn : function(property, value, anyMatch){
23218         if(!value.exec){ // not a regex
23219             value = String(value);
23220             if(value.length == 0){
23221                 return false;
23222             }
23223             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23224         }
23225         return function(r){
23226             return value.test(r.data[property]);
23227         };
23228     },
23229
23230     /**
23231      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23232      * @param {String} property A field on your records
23233      * @param {Number} start The record index to start at (defaults to 0)
23234      * @param {Number} end The last record index to include (defaults to length - 1)
23235      * @return {Number} The sum
23236      */
23237     sum : function(property, start, end){
23238         var rs = this.data.items, v = 0;
23239         start = start || 0;
23240         end = (end || end === 0) ? end : rs.length-1;
23241
23242         for(var i = start; i <= end; i++){
23243             v += (rs[i].data[property] || 0);
23244         }
23245         return v;
23246     },
23247
23248     /**
23249      * Filter the records by a specified property.
23250      * @param {String} field A field on your records
23251      * @param {String/RegExp} value Either a string that the field
23252      * should start with or a RegExp to test against the field
23253      * @param {Boolean} anyMatch True to match any part not just the beginning
23254      */
23255     filter : function(property, value, anyMatch){
23256         var fn = this.createFilterFn(property, value, anyMatch);
23257         return fn ? this.filterBy(fn) : this.clearFilter();
23258     },
23259
23260     /**
23261      * Filter by a function. The specified function will be called with each
23262      * record in this data source. If the function returns true the record is included,
23263      * otherwise it is filtered.
23264      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23265      * @param {Object} scope (optional) The scope of the function (defaults to this)
23266      */
23267     filterBy : function(fn, scope){
23268         this.snapshot = this.snapshot || this.data;
23269         this.data = this.queryBy(fn, scope||this);
23270         this.fireEvent("datachanged", this);
23271     },
23272
23273     /**
23274      * Query the records by a specified property.
23275      * @param {String} field A field on your records
23276      * @param {String/RegExp} value Either a string that the field
23277      * should start with or a RegExp to test against the field
23278      * @param {Boolean} anyMatch True to match any part not just the beginning
23279      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23280      */
23281     query : function(property, value, anyMatch){
23282         var fn = this.createFilterFn(property, value, anyMatch);
23283         return fn ? this.queryBy(fn) : this.data.clone();
23284     },
23285
23286     /**
23287      * Query by a function. The specified function will be called with each
23288      * record in this data source. If the function returns true the record is included
23289      * in the results.
23290      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23291      * @param {Object} scope (optional) The scope of the function (defaults to this)
23292       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23293      **/
23294     queryBy : function(fn, scope){
23295         var data = this.snapshot || this.data;
23296         return data.filterBy(fn, scope||this);
23297     },
23298
23299     /**
23300      * Collects unique values for a particular dataIndex from this store.
23301      * @param {String} dataIndex The property to collect
23302      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23303      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23304      * @return {Array} An array of the unique values
23305      **/
23306     collect : function(dataIndex, allowNull, bypassFilter){
23307         var d = (bypassFilter === true && this.snapshot) ?
23308                 this.snapshot.items : this.data.items;
23309         var v, sv, r = [], l = {};
23310         for(var i = 0, len = d.length; i < len; i++){
23311             v = d[i].data[dataIndex];
23312             sv = String(v);
23313             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23314                 l[sv] = true;
23315                 r[r.length] = v;
23316             }
23317         }
23318         return r;
23319     },
23320
23321     /**
23322      * Revert to a view of the Record cache with no filtering applied.
23323      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23324      */
23325     clearFilter : function(suppressEvent){
23326         if(this.snapshot && this.snapshot != this.data){
23327             this.data = this.snapshot;
23328             delete this.snapshot;
23329             if(suppressEvent !== true){
23330                 this.fireEvent("datachanged", this);
23331             }
23332         }
23333     },
23334
23335     // private
23336     afterEdit : function(record){
23337         if(this.modified.indexOf(record) == -1){
23338             this.modified.push(record);
23339         }
23340         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23341     },
23342     
23343     // private
23344     afterReject : function(record){
23345         this.modified.remove(record);
23346         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23347     },
23348
23349     // private
23350     afterCommit : function(record){
23351         this.modified.remove(record);
23352         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23353     },
23354
23355     /**
23356      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23357      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23358      */
23359     commitChanges : function(){
23360         var m = this.modified.slice(0);
23361         this.modified = [];
23362         for(var i = 0, len = m.length; i < len; i++){
23363             m[i].commit();
23364         }
23365     },
23366
23367     /**
23368      * Cancel outstanding changes on all changed records.
23369      */
23370     rejectChanges : 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].reject();
23375         }
23376     },
23377
23378     onMetaChange : function(meta, rtype, o){
23379         this.recordType = rtype;
23380         this.fields = rtype.prototype.fields;
23381         delete this.snapshot;
23382         this.sortInfo = meta.sortInfo || this.sortInfo;
23383         this.modified = [];
23384         this.fireEvent('metachange', this, this.reader.meta);
23385     },
23386     
23387     moveIndex : function(data, type)
23388     {
23389         var index = this.indexOf(data);
23390         
23391         var newIndex = index + type;
23392         
23393         this.remove(data);
23394         
23395         this.insert(newIndex, data);
23396         
23397     }
23398 });/*
23399  * Based on:
23400  * Ext JS Library 1.1.1
23401  * Copyright(c) 2006-2007, Ext JS, LLC.
23402  *
23403  * Originally Released Under LGPL - original licence link has changed is not relivant.
23404  *
23405  * Fork - LGPL
23406  * <script type="text/javascript">
23407  */
23408
23409 /**
23410  * @class Roo.data.SimpleStore
23411  * @extends Roo.data.Store
23412  * Small helper class to make creating Stores from Array data easier.
23413  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23414  * @cfg {Array} fields An array of field definition objects, or field name strings.
23415  * @cfg {Array} data The multi-dimensional array of data
23416  * @constructor
23417  * @param {Object} config
23418  */
23419 Roo.data.SimpleStore = function(config){
23420     Roo.data.SimpleStore.superclass.constructor.call(this, {
23421         isLocal : true,
23422         reader: new Roo.data.ArrayReader({
23423                 id: config.id
23424             },
23425             Roo.data.Record.create(config.fields)
23426         ),
23427         proxy : new Roo.data.MemoryProxy(config.data)
23428     });
23429     this.load();
23430 };
23431 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23432  * Based on:
23433  * Ext JS Library 1.1.1
23434  * Copyright(c) 2006-2007, Ext JS, LLC.
23435  *
23436  * Originally Released Under LGPL - original licence link has changed is not relivant.
23437  *
23438  * Fork - LGPL
23439  * <script type="text/javascript">
23440  */
23441
23442 /**
23443 /**
23444  * @extends Roo.data.Store
23445  * @class Roo.data.JsonStore
23446  * Small helper class to make creating Stores for JSON data easier. <br/>
23447 <pre><code>
23448 var store = new Roo.data.JsonStore({
23449     url: 'get-images.php',
23450     root: 'images',
23451     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23452 });
23453 </code></pre>
23454  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23455  * JsonReader and HttpProxy (unless inline data is provided).</b>
23456  * @cfg {Array} fields An array of field definition objects, or field name strings.
23457  * @constructor
23458  * @param {Object} config
23459  */
23460 Roo.data.JsonStore = function(c){
23461     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23462         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23463         reader: new Roo.data.JsonReader(c, c.fields)
23464     }));
23465 };
23466 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23467  * Based on:
23468  * Ext JS Library 1.1.1
23469  * Copyright(c) 2006-2007, Ext JS, LLC.
23470  *
23471  * Originally Released Under LGPL - original licence link has changed is not relivant.
23472  *
23473  * Fork - LGPL
23474  * <script type="text/javascript">
23475  */
23476
23477  
23478 Roo.data.Field = function(config){
23479     if(typeof config == "string"){
23480         config = {name: config};
23481     }
23482     Roo.apply(this, config);
23483     
23484     if(!this.type){
23485         this.type = "auto";
23486     }
23487     
23488     var st = Roo.data.SortTypes;
23489     // named sortTypes are supported, here we look them up
23490     if(typeof this.sortType == "string"){
23491         this.sortType = st[this.sortType];
23492     }
23493     
23494     // set default sortType for strings and dates
23495     if(!this.sortType){
23496         switch(this.type){
23497             case "string":
23498                 this.sortType = st.asUCString;
23499                 break;
23500             case "date":
23501                 this.sortType = st.asDate;
23502                 break;
23503             default:
23504                 this.sortType = st.none;
23505         }
23506     }
23507
23508     // define once
23509     var stripRe = /[\$,%]/g;
23510
23511     // prebuilt conversion function for this field, instead of
23512     // switching every time we're reading a value
23513     if(!this.convert){
23514         var cv, dateFormat = this.dateFormat;
23515         switch(this.type){
23516             case "":
23517             case "auto":
23518             case undefined:
23519                 cv = function(v){ return v; };
23520                 break;
23521             case "string":
23522                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23523                 break;
23524             case "int":
23525                 cv = function(v){
23526                     return v !== undefined && v !== null && v !== '' ?
23527                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23528                     };
23529                 break;
23530             case "float":
23531                 cv = function(v){
23532                     return v !== undefined && v !== null && v !== '' ?
23533                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23534                     };
23535                 break;
23536             case "bool":
23537             case "boolean":
23538                 cv = function(v){ return v === true || v === "true" || v == 1; };
23539                 break;
23540             case "date":
23541                 cv = function(v){
23542                     if(!v){
23543                         return '';
23544                     }
23545                     if(v instanceof Date){
23546                         return v;
23547                     }
23548                     if(dateFormat){
23549                         if(dateFormat == "timestamp"){
23550                             return new Date(v*1000);
23551                         }
23552                         return Date.parseDate(v, dateFormat);
23553                     }
23554                     var parsed = Date.parse(v);
23555                     return parsed ? new Date(parsed) : null;
23556                 };
23557              break;
23558             
23559         }
23560         this.convert = cv;
23561     }
23562 };
23563
23564 Roo.data.Field.prototype = {
23565     dateFormat: null,
23566     defaultValue: "",
23567     mapping: null,
23568     sortType : null,
23569     sortDir : "ASC"
23570 };/*
23571  * Based on:
23572  * Ext JS Library 1.1.1
23573  * Copyright(c) 2006-2007, Ext JS, LLC.
23574  *
23575  * Originally Released Under LGPL - original licence link has changed is not relivant.
23576  *
23577  * Fork - LGPL
23578  * <script type="text/javascript">
23579  */
23580  
23581 // Base class for reading structured data from a data source.  This class is intended to be
23582 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23583
23584 /**
23585  * @class Roo.data.DataReader
23586  * Base class for reading structured data from a data source.  This class is intended to be
23587  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23588  */
23589
23590 Roo.data.DataReader = function(meta, recordType){
23591     
23592     this.meta = meta;
23593     
23594     this.recordType = recordType instanceof Array ? 
23595         Roo.data.Record.create(recordType) : recordType;
23596 };
23597
23598 Roo.data.DataReader.prototype = {
23599      /**
23600      * Create an empty record
23601      * @param {Object} data (optional) - overlay some values
23602      * @return {Roo.data.Record} record created.
23603      */
23604     newRow :  function(d) {
23605         var da =  {};
23606         this.recordType.prototype.fields.each(function(c) {
23607             switch( c.type) {
23608                 case 'int' : da[c.name] = 0; break;
23609                 case 'date' : da[c.name] = new Date(); break;
23610                 case 'float' : da[c.name] = 0.0; break;
23611                 case 'boolean' : da[c.name] = false; break;
23612                 default : da[c.name] = ""; break;
23613             }
23614             
23615         });
23616         return new this.recordType(Roo.apply(da, d));
23617     }
23618     
23619 };/*
23620  * Based on:
23621  * Ext JS Library 1.1.1
23622  * Copyright(c) 2006-2007, Ext JS, LLC.
23623  *
23624  * Originally Released Under LGPL - original licence link has changed is not relivant.
23625  *
23626  * Fork - LGPL
23627  * <script type="text/javascript">
23628  */
23629
23630 /**
23631  * @class Roo.data.DataProxy
23632  * @extends Roo.data.Observable
23633  * This class is an abstract base class for implementations which provide retrieval of
23634  * unformatted data objects.<br>
23635  * <p>
23636  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23637  * (of the appropriate type which knows how to parse the data object) to provide a block of
23638  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23639  * <p>
23640  * Custom implementations must implement the load method as described in
23641  * {@link Roo.data.HttpProxy#load}.
23642  */
23643 Roo.data.DataProxy = function(){
23644     this.addEvents({
23645         /**
23646          * @event beforeload
23647          * Fires before a network request is made to retrieve a data object.
23648          * @param {Object} This DataProxy object.
23649          * @param {Object} params The params parameter to the load function.
23650          */
23651         beforeload : true,
23652         /**
23653          * @event load
23654          * Fires before the load method's callback is called.
23655          * @param {Object} This DataProxy object.
23656          * @param {Object} o The data object.
23657          * @param {Object} arg The callback argument object passed to the load function.
23658          */
23659         load : true,
23660         /**
23661          * @event loadexception
23662          * Fires if an Exception occurs during data retrieval.
23663          * @param {Object} This DataProxy object.
23664          * @param {Object} o The data object.
23665          * @param {Object} arg The callback argument object passed to the load function.
23666          * @param {Object} e The Exception.
23667          */
23668         loadexception : true
23669     });
23670     Roo.data.DataProxy.superclass.constructor.call(this);
23671 };
23672
23673 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23674
23675     /**
23676      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23677      */
23678 /*
23679  * Based on:
23680  * Ext JS Library 1.1.1
23681  * Copyright(c) 2006-2007, Ext JS, LLC.
23682  *
23683  * Originally Released Under LGPL - original licence link has changed is not relivant.
23684  *
23685  * Fork - LGPL
23686  * <script type="text/javascript">
23687  */
23688 /**
23689  * @class Roo.data.MemoryProxy
23690  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23691  * to the Reader when its load method is called.
23692  * @constructor
23693  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23694  */
23695 Roo.data.MemoryProxy = function(data){
23696     if (data.data) {
23697         data = data.data;
23698     }
23699     Roo.data.MemoryProxy.superclass.constructor.call(this);
23700     this.data = data;
23701 };
23702
23703 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23704     
23705     /**
23706      * Load data from the requested source (in this case an in-memory
23707      * data object passed to the constructor), read the data object into
23708      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23709      * process that block using the passed callback.
23710      * @param {Object} params This parameter is not used by the MemoryProxy class.
23711      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23712      * object into a block of Roo.data.Records.
23713      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23714      * The function must be passed <ul>
23715      * <li>The Record block object</li>
23716      * <li>The "arg" argument from the load function</li>
23717      * <li>A boolean success indicator</li>
23718      * </ul>
23719      * @param {Object} scope The scope in which to call the callback
23720      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23721      */
23722     load : function(params, reader, callback, scope, arg){
23723         params = params || {};
23724         var result;
23725         try {
23726             result = reader.readRecords(this.data);
23727         }catch(e){
23728             this.fireEvent("loadexception", this, arg, null, e);
23729             callback.call(scope, null, arg, false);
23730             return;
23731         }
23732         callback.call(scope, result, arg, true);
23733     },
23734     
23735     // private
23736     update : function(params, records){
23737         
23738     }
23739 });/*
23740  * Based on:
23741  * Ext JS Library 1.1.1
23742  * Copyright(c) 2006-2007, Ext JS, LLC.
23743  *
23744  * Originally Released Under LGPL - original licence link has changed is not relivant.
23745  *
23746  * Fork - LGPL
23747  * <script type="text/javascript">
23748  */
23749 /**
23750  * @class Roo.data.HttpProxy
23751  * @extends Roo.data.DataProxy
23752  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23753  * configured to reference a certain URL.<br><br>
23754  * <p>
23755  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23756  * from which the running page was served.<br><br>
23757  * <p>
23758  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23759  * <p>
23760  * Be aware that to enable the browser to parse an XML document, the server must set
23761  * the Content-Type header in the HTTP response to "text/xml".
23762  * @constructor
23763  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23764  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23765  * will be used to make the request.
23766  */
23767 Roo.data.HttpProxy = function(conn){
23768     Roo.data.HttpProxy.superclass.constructor.call(this);
23769     // is conn a conn config or a real conn?
23770     this.conn = conn;
23771     this.useAjax = !conn || !conn.events;
23772   
23773 };
23774
23775 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23776     // thse are take from connection...
23777     
23778     /**
23779      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23780      */
23781     /**
23782      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23783      * extra parameters to each request made by this object. (defaults to undefined)
23784      */
23785     /**
23786      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23787      *  to each request made by this object. (defaults to undefined)
23788      */
23789     /**
23790      * @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)
23791      */
23792     /**
23793      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23794      */
23795      /**
23796      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23797      * @type Boolean
23798      */
23799   
23800
23801     /**
23802      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23803      * @type Boolean
23804      */
23805     /**
23806      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23807      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23808      * a finer-grained basis than the DataProxy events.
23809      */
23810     getConnection : function(){
23811         return this.useAjax ? Roo.Ajax : this.conn;
23812     },
23813
23814     /**
23815      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23816      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23817      * process that block using the passed callback.
23818      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23819      * for the request to the remote server.
23820      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23821      * object into a block of Roo.data.Records.
23822      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23823      * The function must be passed <ul>
23824      * <li>The Record block object</li>
23825      * <li>The "arg" argument from the load function</li>
23826      * <li>A boolean success indicator</li>
23827      * </ul>
23828      * @param {Object} scope The scope in which to call the callback
23829      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23830      */
23831     load : function(params, reader, callback, scope, arg){
23832         if(this.fireEvent("beforeload", this, params) !== false){
23833             var  o = {
23834                 params : params || {},
23835                 request: {
23836                     callback : callback,
23837                     scope : scope,
23838                     arg : arg
23839                 },
23840                 reader: reader,
23841                 callback : this.loadResponse,
23842                 scope: this
23843             };
23844             if(this.useAjax){
23845                 Roo.applyIf(o, this.conn);
23846                 if(this.activeRequest){
23847                     Roo.Ajax.abort(this.activeRequest);
23848                 }
23849                 this.activeRequest = Roo.Ajax.request(o);
23850             }else{
23851                 this.conn.request(o);
23852             }
23853         }else{
23854             callback.call(scope||this, null, arg, false);
23855         }
23856     },
23857
23858     // private
23859     loadResponse : function(o, success, response){
23860         delete this.activeRequest;
23861         if(!success){
23862             this.fireEvent("loadexception", this, o, response);
23863             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23864             return;
23865         }
23866         var result;
23867         try {
23868             result = o.reader.read(response);
23869         }catch(e){
23870             this.fireEvent("loadexception", this, o, response, e);
23871             o.request.callback.call(o.request.scope, null, o.request.arg, false);
23872             return;
23873         }
23874         
23875         this.fireEvent("load", this, o, o.request.arg);
23876         o.request.callback.call(o.request.scope, result, o.request.arg, true);
23877     },
23878
23879     // private
23880     update : function(dataSet){
23881
23882     },
23883
23884     // private
23885     updateResponse : function(dataSet){
23886
23887     }
23888 });/*
23889  * Based on:
23890  * Ext JS Library 1.1.1
23891  * Copyright(c) 2006-2007, Ext JS, LLC.
23892  *
23893  * Originally Released Under LGPL - original licence link has changed is not relivant.
23894  *
23895  * Fork - LGPL
23896  * <script type="text/javascript">
23897  */
23898
23899 /**
23900  * @class Roo.data.ScriptTagProxy
23901  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
23902  * other than the originating domain of the running page.<br><br>
23903  * <p>
23904  * <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
23905  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
23906  * <p>
23907  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
23908  * source code that is used as the source inside a &lt;script> tag.<br><br>
23909  * <p>
23910  * In order for the browser to process the returned data, the server must wrap the data object
23911  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
23912  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
23913  * depending on whether the callback name was passed:
23914  * <p>
23915  * <pre><code>
23916 boolean scriptTag = false;
23917 String cb = request.getParameter("callback");
23918 if (cb != null) {
23919     scriptTag = true;
23920     response.setContentType("text/javascript");
23921 } else {
23922     response.setContentType("application/x-json");
23923 }
23924 Writer out = response.getWriter();
23925 if (scriptTag) {
23926     out.write(cb + "(");
23927 }
23928 out.print(dataBlock.toJsonString());
23929 if (scriptTag) {
23930     out.write(");");
23931 }
23932 </pre></code>
23933  *
23934  * @constructor
23935  * @param {Object} config A configuration object.
23936  */
23937 Roo.data.ScriptTagProxy = function(config){
23938     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
23939     Roo.apply(this, config);
23940     this.head = document.getElementsByTagName("head")[0];
23941 };
23942
23943 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
23944
23945 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
23946     /**
23947      * @cfg {String} url The URL from which to request the data object.
23948      */
23949     /**
23950      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
23951      */
23952     timeout : 30000,
23953     /**
23954      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
23955      * the server the name of the callback function set up by the load call to process the returned data object.
23956      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
23957      * javascript output which calls this named function passing the data object as its only parameter.
23958      */
23959     callbackParam : "callback",
23960     /**
23961      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
23962      * name to the request.
23963      */
23964     nocache : true,
23965
23966     /**
23967      * Load data from the configured URL, read the data object into
23968      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23969      * process that block using the passed callback.
23970      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23971      * for the request to the remote server.
23972      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23973      * object into a block of Roo.data.Records.
23974      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23975      * The function must be passed <ul>
23976      * <li>The Record block object</li>
23977      * <li>The "arg" argument from the load function</li>
23978      * <li>A boolean success indicator</li>
23979      * </ul>
23980      * @param {Object} scope The scope in which to call the callback
23981      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23982      */
23983     load : function(params, reader, callback, scope, arg){
23984         if(this.fireEvent("beforeload", this, params) !== false){
23985
23986             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
23987
23988             var url = this.url;
23989             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
23990             if(this.nocache){
23991                 url += "&_dc=" + (new Date().getTime());
23992             }
23993             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
23994             var trans = {
23995                 id : transId,
23996                 cb : "stcCallback"+transId,
23997                 scriptId : "stcScript"+transId,
23998                 params : params,
23999                 arg : arg,
24000                 url : url,
24001                 callback : callback,
24002                 scope : scope,
24003                 reader : reader
24004             };
24005             var conn = this;
24006
24007             window[trans.cb] = function(o){
24008                 conn.handleResponse(o, trans);
24009             };
24010
24011             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24012
24013             if(this.autoAbort !== false){
24014                 this.abort();
24015             }
24016
24017             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24018
24019             var script = document.createElement("script");
24020             script.setAttribute("src", url);
24021             script.setAttribute("type", "text/javascript");
24022             script.setAttribute("id", trans.scriptId);
24023             this.head.appendChild(script);
24024
24025             this.trans = trans;
24026         }else{
24027             callback.call(scope||this, null, arg, false);
24028         }
24029     },
24030
24031     // private
24032     isLoading : function(){
24033         return this.trans ? true : false;
24034     },
24035
24036     /**
24037      * Abort the current server request.
24038      */
24039     abort : function(){
24040         if(this.isLoading()){
24041             this.destroyTrans(this.trans);
24042         }
24043     },
24044
24045     // private
24046     destroyTrans : function(trans, isLoaded){
24047         this.head.removeChild(document.getElementById(trans.scriptId));
24048         clearTimeout(trans.timeoutId);
24049         if(isLoaded){
24050             window[trans.cb] = undefined;
24051             try{
24052                 delete window[trans.cb];
24053             }catch(e){}
24054         }else{
24055             // if hasn't been loaded, wait for load to remove it to prevent script error
24056             window[trans.cb] = function(){
24057                 window[trans.cb] = undefined;
24058                 try{
24059                     delete window[trans.cb];
24060                 }catch(e){}
24061             };
24062         }
24063     },
24064
24065     // private
24066     handleResponse : function(o, trans){
24067         this.trans = false;
24068         this.destroyTrans(trans, true);
24069         var result;
24070         try {
24071             result = trans.reader.readRecords(o);
24072         }catch(e){
24073             this.fireEvent("loadexception", this, o, trans.arg, e);
24074             trans.callback.call(trans.scope||window, null, trans.arg, false);
24075             return;
24076         }
24077         this.fireEvent("load", this, o, trans.arg);
24078         trans.callback.call(trans.scope||window, result, trans.arg, true);
24079     },
24080
24081     // private
24082     handleFailure : function(trans){
24083         this.trans = false;
24084         this.destroyTrans(trans, false);
24085         this.fireEvent("loadexception", this, null, trans.arg);
24086         trans.callback.call(trans.scope||window, null, trans.arg, false);
24087     }
24088 });/*
24089  * Based on:
24090  * Ext JS Library 1.1.1
24091  * Copyright(c) 2006-2007, Ext JS, LLC.
24092  *
24093  * Originally Released Under LGPL - original licence link has changed is not relivant.
24094  *
24095  * Fork - LGPL
24096  * <script type="text/javascript">
24097  */
24098
24099 /**
24100  * @class Roo.data.JsonReader
24101  * @extends Roo.data.DataReader
24102  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24103  * based on mappings in a provided Roo.data.Record constructor.
24104  * 
24105  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24106  * in the reply previously. 
24107  * 
24108  * <p>
24109  * Example code:
24110  * <pre><code>
24111 var RecordDef = Roo.data.Record.create([
24112     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24113     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24114 ]);
24115 var myReader = new Roo.data.JsonReader({
24116     totalProperty: "results",    // The property which contains the total dataset size (optional)
24117     root: "rows",                // The property which contains an Array of row objects
24118     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24119 }, RecordDef);
24120 </code></pre>
24121  * <p>
24122  * This would consume a JSON file like this:
24123  * <pre><code>
24124 { 'results': 2, 'rows': [
24125     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24126     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24127 }
24128 </code></pre>
24129  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24130  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24131  * paged from the remote server.
24132  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24133  * @cfg {String} root name of the property which contains the Array of row objects.
24134  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24135  * @cfg {Array} fields Array of field definition objects
24136  * @constructor
24137  * Create a new JsonReader
24138  * @param {Object} meta Metadata configuration options
24139  * @param {Object} recordType Either an Array of field definition objects,
24140  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24141  */
24142 Roo.data.JsonReader = function(meta, recordType){
24143     
24144     meta = meta || {};
24145     // set some defaults:
24146     Roo.applyIf(meta, {
24147         totalProperty: 'total',
24148         successProperty : 'success',
24149         root : 'data',
24150         id : 'id'
24151     });
24152     
24153     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24154 };
24155 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24156     
24157     /**
24158      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24159      * Used by Store query builder to append _requestMeta to params.
24160      * 
24161      */
24162     metaFromRemote : false,
24163     /**
24164      * This method is only used by a DataProxy which has retrieved data from a remote server.
24165      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24166      * @return {Object} data A data block which is used by an Roo.data.Store object as
24167      * a cache of Roo.data.Records.
24168      */
24169     read : function(response){
24170         var json = response.responseText;
24171        
24172         var o = /* eval:var:o */ eval("("+json+")");
24173         if(!o) {
24174             throw {message: "JsonReader.read: Json object not found"};
24175         }
24176         
24177         if(o.metaData){
24178             
24179             delete this.ef;
24180             this.metaFromRemote = true;
24181             this.meta = o.metaData;
24182             this.recordType = Roo.data.Record.create(o.metaData.fields);
24183             this.onMetaChange(this.meta, this.recordType, o);
24184         }
24185         return this.readRecords(o);
24186     },
24187
24188     // private function a store will implement
24189     onMetaChange : function(meta, recordType, o){
24190
24191     },
24192
24193     /**
24194          * @ignore
24195          */
24196     simpleAccess: function(obj, subsc) {
24197         return obj[subsc];
24198     },
24199
24200         /**
24201          * @ignore
24202          */
24203     getJsonAccessor: function(){
24204         var re = /[\[\.]/;
24205         return function(expr) {
24206             try {
24207                 return(re.test(expr))
24208                     ? new Function("obj", "return obj." + expr)
24209                     : function(obj){
24210                         return obj[expr];
24211                     };
24212             } catch(e){}
24213             return Roo.emptyFn;
24214         };
24215     }(),
24216
24217     /**
24218      * Create a data block containing Roo.data.Records from an XML document.
24219      * @param {Object} o An object which contains an Array of row objects in the property specified
24220      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24221      * which contains the total size of the dataset.
24222      * @return {Object} data A data block which is used by an Roo.data.Store object as
24223      * a cache of Roo.data.Records.
24224      */
24225     readRecords : function(o){
24226         /**
24227          * After any data loads, the raw JSON data is available for further custom processing.
24228          * @type Object
24229          */
24230         this.o = o;
24231         var s = this.meta, Record = this.recordType,
24232             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24233
24234 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24235         if (!this.ef) {
24236             if(s.totalProperty) {
24237                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24238                 }
24239                 if(s.successProperty) {
24240                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24241                 }
24242                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24243                 if (s.id) {
24244                         var g = this.getJsonAccessor(s.id);
24245                         this.getId = function(rec) {
24246                                 var r = g(rec);  
24247                                 return (r === undefined || r === "") ? null : r;
24248                         };
24249                 } else {
24250                         this.getId = function(){return null;};
24251                 }
24252             this.ef = [];
24253             for(var jj = 0; jj < fl; jj++){
24254                 f = fi[jj];
24255                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24256                 this.ef[jj] = this.getJsonAccessor(map);
24257             }
24258         }
24259
24260         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24261         if(s.totalProperty){
24262             var vt = parseInt(this.getTotal(o), 10);
24263             if(!isNaN(vt)){
24264                 totalRecords = vt;
24265             }
24266         }
24267         if(s.successProperty){
24268             var vs = this.getSuccess(o);
24269             if(vs === false || vs === 'false'){
24270                 success = false;
24271             }
24272         }
24273         var records = [];
24274         for(var i = 0; i < c; i++){
24275                 var n = root[i];
24276             var values = {};
24277             var id = this.getId(n);
24278             for(var j = 0; j < fl; j++){
24279                 f = fi[j];
24280             var v = this.ef[j](n);
24281             if (!f.convert) {
24282                 Roo.log('missing convert for ' + f.name);
24283                 Roo.log(f);
24284                 continue;
24285             }
24286             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24287             }
24288             var record = new Record(values, id);
24289             record.json = n;
24290             records[i] = record;
24291         }
24292         return {
24293             raw : o,
24294             success : success,
24295             records : records,
24296             totalRecords : totalRecords
24297         };
24298     }
24299 });/*
24300  * Based on:
24301  * Ext JS Library 1.1.1
24302  * Copyright(c) 2006-2007, Ext JS, LLC.
24303  *
24304  * Originally Released Under LGPL - original licence link has changed is not relivant.
24305  *
24306  * Fork - LGPL
24307  * <script type="text/javascript">
24308  */
24309
24310 /**
24311  * @class Roo.data.XmlReader
24312  * @extends Roo.data.DataReader
24313  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24314  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24315  * <p>
24316  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24317  * header in the HTTP response must be set to "text/xml".</em>
24318  * <p>
24319  * Example code:
24320  * <pre><code>
24321 var RecordDef = Roo.data.Record.create([
24322    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24323    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24324 ]);
24325 var myReader = new Roo.data.XmlReader({
24326    totalRecords: "results", // The element which contains the total dataset size (optional)
24327    record: "row",           // The repeated element which contains row information
24328    id: "id"                 // The element within the row that provides an ID for the record (optional)
24329 }, RecordDef);
24330 </code></pre>
24331  * <p>
24332  * This would consume an XML file like this:
24333  * <pre><code>
24334 &lt;?xml?>
24335 &lt;dataset>
24336  &lt;results>2&lt;/results>
24337  &lt;row>
24338    &lt;id>1&lt;/id>
24339    &lt;name>Bill&lt;/name>
24340    &lt;occupation>Gardener&lt;/occupation>
24341  &lt;/row>
24342  &lt;row>
24343    &lt;id>2&lt;/id>
24344    &lt;name>Ben&lt;/name>
24345    &lt;occupation>Horticulturalist&lt;/occupation>
24346  &lt;/row>
24347 &lt;/dataset>
24348 </code></pre>
24349  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24350  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24351  * paged from the remote server.
24352  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24353  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24354  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24355  * a record identifier value.
24356  * @constructor
24357  * Create a new XmlReader
24358  * @param {Object} meta Metadata configuration options
24359  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24360  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24361  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24362  */
24363 Roo.data.XmlReader = function(meta, recordType){
24364     meta = meta || {};
24365     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24366 };
24367 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24368     /**
24369      * This method is only used by a DataProxy which has retrieved data from a remote server.
24370          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24371          * to contain a method called 'responseXML' that returns an XML document object.
24372      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24373      * a cache of Roo.data.Records.
24374      */
24375     read : function(response){
24376         var doc = response.responseXML;
24377         if(!doc) {
24378             throw {message: "XmlReader.read: XML Document not available"};
24379         }
24380         return this.readRecords(doc);
24381     },
24382
24383     /**
24384      * Create a data block containing Roo.data.Records from an XML document.
24385          * @param {Object} doc A parsed XML document.
24386      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24387      * a cache of Roo.data.Records.
24388      */
24389     readRecords : function(doc){
24390         /**
24391          * After any data loads/reads, the raw XML Document is available for further custom processing.
24392          * @type XMLDocument
24393          */
24394         this.xmlData = doc;
24395         var root = doc.documentElement || doc;
24396         var q = Roo.DomQuery;
24397         var recordType = this.recordType, fields = recordType.prototype.fields;
24398         var sid = this.meta.id;
24399         var totalRecords = 0, success = true;
24400         if(this.meta.totalRecords){
24401             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24402         }
24403         
24404         if(this.meta.success){
24405             var sv = q.selectValue(this.meta.success, root, true);
24406             success = sv !== false && sv !== 'false';
24407         }
24408         var records = [];
24409         var ns = q.select(this.meta.record, root);
24410         for(var i = 0, len = ns.length; i < len; i++) {
24411                 var n = ns[i];
24412                 var values = {};
24413                 var id = sid ? q.selectValue(sid, n) : undefined;
24414                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24415                     var f = fields.items[j];
24416                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24417                     v = f.convert(v);
24418                     values[f.name] = v;
24419                 }
24420                 var record = new recordType(values, id);
24421                 record.node = n;
24422                 records[records.length] = record;
24423             }
24424
24425             return {
24426                 success : success,
24427                 records : records,
24428                 totalRecords : totalRecords || records.length
24429             };
24430     }
24431 });/*
24432  * Based on:
24433  * Ext JS Library 1.1.1
24434  * Copyright(c) 2006-2007, Ext JS, LLC.
24435  *
24436  * Originally Released Under LGPL - original licence link has changed is not relivant.
24437  *
24438  * Fork - LGPL
24439  * <script type="text/javascript">
24440  */
24441
24442 /**
24443  * @class Roo.data.ArrayReader
24444  * @extends Roo.data.DataReader
24445  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24446  * Each element of that Array represents a row of data fields. The
24447  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24448  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24449  * <p>
24450  * Example code:.
24451  * <pre><code>
24452 var RecordDef = Roo.data.Record.create([
24453     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24454     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24455 ]);
24456 var myReader = new Roo.data.ArrayReader({
24457     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24458 }, RecordDef);
24459 </code></pre>
24460  * <p>
24461  * This would consume an Array like this:
24462  * <pre><code>
24463 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24464   </code></pre>
24465  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
24466  * @constructor
24467  * Create a new JsonReader
24468  * @param {Object} meta Metadata configuration options.
24469  * @param {Object} recordType Either an Array of field definition objects
24470  * as specified to {@link Roo.data.Record#create},
24471  * or an {@link Roo.data.Record} object
24472  * created using {@link Roo.data.Record#create}.
24473  */
24474 Roo.data.ArrayReader = function(meta, recordType){
24475     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
24476 };
24477
24478 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24479     /**
24480      * Create a data block containing Roo.data.Records from an XML document.
24481      * @param {Object} o An Array of row objects which represents the dataset.
24482      * @return {Object} data A data block which is used by an Roo.data.Store object as
24483      * a cache of Roo.data.Records.
24484      */
24485     readRecords : function(o){
24486         var sid = this.meta ? this.meta.id : null;
24487         var recordType = this.recordType, fields = recordType.prototype.fields;
24488         var records = [];
24489         var root = o;
24490             for(var i = 0; i < root.length; i++){
24491                     var n = root[i];
24492                 var values = {};
24493                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24494                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24495                 var f = fields.items[j];
24496                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24497                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24498                 v = f.convert(v);
24499                 values[f.name] = v;
24500             }
24501                 var record = new recordType(values, id);
24502                 record.json = n;
24503                 records[records.length] = record;
24504             }
24505             return {
24506                 records : records,
24507                 totalRecords : records.length
24508             };
24509     }
24510 });/*
24511  * Based on:
24512  * Ext JS Library 1.1.1
24513  * Copyright(c) 2006-2007, Ext JS, LLC.
24514  *
24515  * Originally Released Under LGPL - original licence link has changed is not relivant.
24516  *
24517  * Fork - LGPL
24518  * <script type="text/javascript">
24519  */
24520
24521
24522 /**
24523  * @class Roo.data.Tree
24524  * @extends Roo.util.Observable
24525  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24526  * in the tree have most standard DOM functionality.
24527  * @constructor
24528  * @param {Node} root (optional) The root node
24529  */
24530 Roo.data.Tree = function(root){
24531    this.nodeHash = {};
24532    /**
24533     * The root node for this tree
24534     * @type Node
24535     */
24536    this.root = null;
24537    if(root){
24538        this.setRootNode(root);
24539    }
24540    this.addEvents({
24541        /**
24542         * @event append
24543         * Fires when a new child node is appended to a node in this tree.
24544         * @param {Tree} tree The owner tree
24545         * @param {Node} parent The parent node
24546         * @param {Node} node The newly appended node
24547         * @param {Number} index The index of the newly appended node
24548         */
24549        "append" : true,
24550        /**
24551         * @event remove
24552         * Fires when a child node is removed from a node in this tree.
24553         * @param {Tree} tree The owner tree
24554         * @param {Node} parent The parent node
24555         * @param {Node} node The child node removed
24556         */
24557        "remove" : true,
24558        /**
24559         * @event move
24560         * Fires when a node is moved to a new location in the tree
24561         * @param {Tree} tree The owner tree
24562         * @param {Node} node The node moved
24563         * @param {Node} oldParent The old parent of this node
24564         * @param {Node} newParent The new parent of this node
24565         * @param {Number} index The index it was moved to
24566         */
24567        "move" : true,
24568        /**
24569         * @event insert
24570         * Fires when a new child node is inserted in a node in this tree.
24571         * @param {Tree} tree The owner tree
24572         * @param {Node} parent The parent node
24573         * @param {Node} node The child node inserted
24574         * @param {Node} refNode The child node the node was inserted before
24575         */
24576        "insert" : true,
24577        /**
24578         * @event beforeappend
24579         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24580         * @param {Tree} tree The owner tree
24581         * @param {Node} parent The parent node
24582         * @param {Node} node The child node to be appended
24583         */
24584        "beforeappend" : true,
24585        /**
24586         * @event beforeremove
24587         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24588         * @param {Tree} tree The owner tree
24589         * @param {Node} parent The parent node
24590         * @param {Node} node The child node to be removed
24591         */
24592        "beforeremove" : true,
24593        /**
24594         * @event beforemove
24595         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24596         * @param {Tree} tree The owner tree
24597         * @param {Node} node The node being moved
24598         * @param {Node} oldParent The parent of the node
24599         * @param {Node} newParent The new parent the node is moving to
24600         * @param {Number} index The index it is being moved to
24601         */
24602        "beforemove" : true,
24603        /**
24604         * @event beforeinsert
24605         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24606         * @param {Tree} tree The owner tree
24607         * @param {Node} parent The parent node
24608         * @param {Node} node The child node to be inserted
24609         * @param {Node} refNode The child node the node is being inserted before
24610         */
24611        "beforeinsert" : true
24612    });
24613
24614     Roo.data.Tree.superclass.constructor.call(this);
24615 };
24616
24617 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24618     pathSeparator: "/",
24619
24620     proxyNodeEvent : function(){
24621         return this.fireEvent.apply(this, arguments);
24622     },
24623
24624     /**
24625      * Returns the root node for this tree.
24626      * @return {Node}
24627      */
24628     getRootNode : function(){
24629         return this.root;
24630     },
24631
24632     /**
24633      * Sets the root node for this tree.
24634      * @param {Node} node
24635      * @return {Node}
24636      */
24637     setRootNode : function(node){
24638         this.root = node;
24639         node.ownerTree = this;
24640         node.isRoot = true;
24641         this.registerNode(node);
24642         return node;
24643     },
24644
24645     /**
24646      * Gets a node in this tree by its id.
24647      * @param {String} id
24648      * @return {Node}
24649      */
24650     getNodeById : function(id){
24651         return this.nodeHash[id];
24652     },
24653
24654     registerNode : function(node){
24655         this.nodeHash[node.id] = node;
24656     },
24657
24658     unregisterNode : function(node){
24659         delete this.nodeHash[node.id];
24660     },
24661
24662     toString : function(){
24663         return "[Tree"+(this.id?" "+this.id:"")+"]";
24664     }
24665 });
24666
24667 /**
24668  * @class Roo.data.Node
24669  * @extends Roo.util.Observable
24670  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24671  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24672  * @constructor
24673  * @param {Object} attributes The attributes/config for the node
24674  */
24675 Roo.data.Node = function(attributes){
24676     /**
24677      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24678      * @type {Object}
24679      */
24680     this.attributes = attributes || {};
24681     this.leaf = this.attributes.leaf;
24682     /**
24683      * The node id. @type String
24684      */
24685     this.id = this.attributes.id;
24686     if(!this.id){
24687         this.id = Roo.id(null, "ynode-");
24688         this.attributes.id = this.id;
24689     }
24690      
24691     
24692     /**
24693      * All child nodes of this node. @type Array
24694      */
24695     this.childNodes = [];
24696     if(!this.childNodes.indexOf){ // indexOf is a must
24697         this.childNodes.indexOf = function(o){
24698             for(var i = 0, len = this.length; i < len; i++){
24699                 if(this[i] == o) {
24700                     return i;
24701                 }
24702             }
24703             return -1;
24704         };
24705     }
24706     /**
24707      * The parent node for this node. @type Node
24708      */
24709     this.parentNode = null;
24710     /**
24711      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24712      */
24713     this.firstChild = null;
24714     /**
24715      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24716      */
24717     this.lastChild = null;
24718     /**
24719      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24720      */
24721     this.previousSibling = null;
24722     /**
24723      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24724      */
24725     this.nextSibling = null;
24726
24727     this.addEvents({
24728        /**
24729         * @event append
24730         * Fires when a new child node is appended
24731         * @param {Tree} tree The owner tree
24732         * @param {Node} this This node
24733         * @param {Node} node The newly appended node
24734         * @param {Number} index The index of the newly appended node
24735         */
24736        "append" : true,
24737        /**
24738         * @event remove
24739         * Fires when a child node is removed
24740         * @param {Tree} tree The owner tree
24741         * @param {Node} this This node
24742         * @param {Node} node The removed node
24743         */
24744        "remove" : true,
24745        /**
24746         * @event move
24747         * Fires when this node is moved to a new location in the tree
24748         * @param {Tree} tree The owner tree
24749         * @param {Node} this This node
24750         * @param {Node} oldParent The old parent of this node
24751         * @param {Node} newParent The new parent of this node
24752         * @param {Number} index The index it was moved to
24753         */
24754        "move" : true,
24755        /**
24756         * @event insert
24757         * Fires when a new child node is inserted.
24758         * @param {Tree} tree The owner tree
24759         * @param {Node} this This node
24760         * @param {Node} node The child node inserted
24761         * @param {Node} refNode The child node the node was inserted before
24762         */
24763        "insert" : true,
24764        /**
24765         * @event beforeappend
24766         * Fires before a new child is appended, return false to cancel the append.
24767         * @param {Tree} tree The owner tree
24768         * @param {Node} this This node
24769         * @param {Node} node The child node to be appended
24770         */
24771        "beforeappend" : true,
24772        /**
24773         * @event beforeremove
24774         * Fires before a child is removed, return false to cancel the remove.
24775         * @param {Tree} tree The owner tree
24776         * @param {Node} this This node
24777         * @param {Node} node The child node to be removed
24778         */
24779        "beforeremove" : true,
24780        /**
24781         * @event beforemove
24782         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24783         * @param {Tree} tree The owner tree
24784         * @param {Node} this This node
24785         * @param {Node} oldParent The parent of this node
24786         * @param {Node} newParent The new parent this node is moving to
24787         * @param {Number} index The index it is being moved to
24788         */
24789        "beforemove" : true,
24790        /**
24791         * @event beforeinsert
24792         * Fires before a new child is inserted, return false to cancel the insert.
24793         * @param {Tree} tree The owner tree
24794         * @param {Node} this This node
24795         * @param {Node} node The child node to be inserted
24796         * @param {Node} refNode The child node the node is being inserted before
24797         */
24798        "beforeinsert" : true
24799    });
24800     this.listeners = this.attributes.listeners;
24801     Roo.data.Node.superclass.constructor.call(this);
24802 };
24803
24804 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24805     fireEvent : function(evtName){
24806         // first do standard event for this node
24807         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24808             return false;
24809         }
24810         // then bubble it up to the tree if the event wasn't cancelled
24811         var ot = this.getOwnerTree();
24812         if(ot){
24813             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24814                 return false;
24815             }
24816         }
24817         return true;
24818     },
24819
24820     /**
24821      * Returns true if this node is a leaf
24822      * @return {Boolean}
24823      */
24824     isLeaf : function(){
24825         return this.leaf === true;
24826     },
24827
24828     // private
24829     setFirstChild : function(node){
24830         this.firstChild = node;
24831     },
24832
24833     //private
24834     setLastChild : function(node){
24835         this.lastChild = node;
24836     },
24837
24838
24839     /**
24840      * Returns true if this node is the last child of its parent
24841      * @return {Boolean}
24842      */
24843     isLast : function(){
24844        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24845     },
24846
24847     /**
24848      * Returns true if this node is the first child of its parent
24849      * @return {Boolean}
24850      */
24851     isFirst : function(){
24852        return (!this.parentNode ? true : this.parentNode.firstChild == this);
24853     },
24854
24855     hasChildNodes : function(){
24856         return !this.isLeaf() && this.childNodes.length > 0;
24857     },
24858
24859     /**
24860      * Insert node(s) as the last child node of this node.
24861      * @param {Node/Array} node The node or Array of nodes to append
24862      * @return {Node} The appended node if single append, or null if an array was passed
24863      */
24864     appendChild : function(node){
24865         var multi = false;
24866         if(node instanceof Array){
24867             multi = node;
24868         }else if(arguments.length > 1){
24869             multi = arguments;
24870         }
24871         // if passed an array or multiple args do them one by one
24872         if(multi){
24873             for(var i = 0, len = multi.length; i < len; i++) {
24874                 this.appendChild(multi[i]);
24875             }
24876         }else{
24877             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
24878                 return false;
24879             }
24880             var index = this.childNodes.length;
24881             var oldParent = node.parentNode;
24882             // it's a move, make sure we move it cleanly
24883             if(oldParent){
24884                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
24885                     return false;
24886                 }
24887                 oldParent.removeChild(node);
24888             }
24889             index = this.childNodes.length;
24890             if(index == 0){
24891                 this.setFirstChild(node);
24892             }
24893             this.childNodes.push(node);
24894             node.parentNode = this;
24895             var ps = this.childNodes[index-1];
24896             if(ps){
24897                 node.previousSibling = ps;
24898                 ps.nextSibling = node;
24899             }else{
24900                 node.previousSibling = null;
24901             }
24902             node.nextSibling = null;
24903             this.setLastChild(node);
24904             node.setOwnerTree(this.getOwnerTree());
24905             this.fireEvent("append", this.ownerTree, this, node, index);
24906             if(oldParent){
24907                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
24908             }
24909             return node;
24910         }
24911     },
24912
24913     /**
24914      * Removes a child node from this node.
24915      * @param {Node} node The node to remove
24916      * @return {Node} The removed node
24917      */
24918     removeChild : function(node){
24919         var index = this.childNodes.indexOf(node);
24920         if(index == -1){
24921             return false;
24922         }
24923         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
24924             return false;
24925         }
24926
24927         // remove it from childNodes collection
24928         this.childNodes.splice(index, 1);
24929
24930         // update siblings
24931         if(node.previousSibling){
24932             node.previousSibling.nextSibling = node.nextSibling;
24933         }
24934         if(node.nextSibling){
24935             node.nextSibling.previousSibling = node.previousSibling;
24936         }
24937
24938         // update child refs
24939         if(this.firstChild == node){
24940             this.setFirstChild(node.nextSibling);
24941         }
24942         if(this.lastChild == node){
24943             this.setLastChild(node.previousSibling);
24944         }
24945
24946         node.setOwnerTree(null);
24947         // clear any references from the node
24948         node.parentNode = null;
24949         node.previousSibling = null;
24950         node.nextSibling = null;
24951         this.fireEvent("remove", this.ownerTree, this, node);
24952         return node;
24953     },
24954
24955     /**
24956      * Inserts the first node before the second node in this nodes childNodes collection.
24957      * @param {Node} node The node to insert
24958      * @param {Node} refNode The node to insert before (if null the node is appended)
24959      * @return {Node} The inserted node
24960      */
24961     insertBefore : function(node, refNode){
24962         if(!refNode){ // like standard Dom, refNode can be null for append
24963             return this.appendChild(node);
24964         }
24965         // nothing to do
24966         if(node == refNode){
24967             return false;
24968         }
24969
24970         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
24971             return false;
24972         }
24973         var index = this.childNodes.indexOf(refNode);
24974         var oldParent = node.parentNode;
24975         var refIndex = index;
24976
24977         // when moving internally, indexes will change after remove
24978         if(oldParent == this && this.childNodes.indexOf(node) < index){
24979             refIndex--;
24980         }
24981
24982         // it's a move, make sure we move it cleanly
24983         if(oldParent){
24984             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
24985                 return false;
24986             }
24987             oldParent.removeChild(node);
24988         }
24989         if(refIndex == 0){
24990             this.setFirstChild(node);
24991         }
24992         this.childNodes.splice(refIndex, 0, node);
24993         node.parentNode = this;
24994         var ps = this.childNodes[refIndex-1];
24995         if(ps){
24996             node.previousSibling = ps;
24997             ps.nextSibling = node;
24998         }else{
24999             node.previousSibling = null;
25000         }
25001         node.nextSibling = refNode;
25002         refNode.previousSibling = node;
25003         node.setOwnerTree(this.getOwnerTree());
25004         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25005         if(oldParent){
25006             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25007         }
25008         return node;
25009     },
25010
25011     /**
25012      * Returns the child node at the specified index.
25013      * @param {Number} index
25014      * @return {Node}
25015      */
25016     item : function(index){
25017         return this.childNodes[index];
25018     },
25019
25020     /**
25021      * Replaces one child node in this node with another.
25022      * @param {Node} newChild The replacement node
25023      * @param {Node} oldChild The node to replace
25024      * @return {Node} The replaced node
25025      */
25026     replaceChild : function(newChild, oldChild){
25027         this.insertBefore(newChild, oldChild);
25028         this.removeChild(oldChild);
25029         return oldChild;
25030     },
25031
25032     /**
25033      * Returns the index of a child node
25034      * @param {Node} node
25035      * @return {Number} The index of the node or -1 if it was not found
25036      */
25037     indexOf : function(child){
25038         return this.childNodes.indexOf(child);
25039     },
25040
25041     /**
25042      * Returns the tree this node is in.
25043      * @return {Tree}
25044      */
25045     getOwnerTree : function(){
25046         // if it doesn't have one, look for one
25047         if(!this.ownerTree){
25048             var p = this;
25049             while(p){
25050                 if(p.ownerTree){
25051                     this.ownerTree = p.ownerTree;
25052                     break;
25053                 }
25054                 p = p.parentNode;
25055             }
25056         }
25057         return this.ownerTree;
25058     },
25059
25060     /**
25061      * Returns depth of this node (the root node has a depth of 0)
25062      * @return {Number}
25063      */
25064     getDepth : function(){
25065         var depth = 0;
25066         var p = this;
25067         while(p.parentNode){
25068             ++depth;
25069             p = p.parentNode;
25070         }
25071         return depth;
25072     },
25073
25074     // private
25075     setOwnerTree : function(tree){
25076         // if it's move, we need to update everyone
25077         if(tree != this.ownerTree){
25078             if(this.ownerTree){
25079                 this.ownerTree.unregisterNode(this);
25080             }
25081             this.ownerTree = tree;
25082             var cs = this.childNodes;
25083             for(var i = 0, len = cs.length; i < len; i++) {
25084                 cs[i].setOwnerTree(tree);
25085             }
25086             if(tree){
25087                 tree.registerNode(this);
25088             }
25089         }
25090     },
25091
25092     /**
25093      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25094      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25095      * @return {String} The path
25096      */
25097     getPath : function(attr){
25098         attr = attr || "id";
25099         var p = this.parentNode;
25100         var b = [this.attributes[attr]];
25101         while(p){
25102             b.unshift(p.attributes[attr]);
25103             p = p.parentNode;
25104         }
25105         var sep = this.getOwnerTree().pathSeparator;
25106         return sep + b.join(sep);
25107     },
25108
25109     /**
25110      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25111      * function call will be the scope provided or the current node. The arguments to the function
25112      * will be the args provided or the current node. If the function returns false at any point,
25113      * the bubble is stopped.
25114      * @param {Function} fn The function to call
25115      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25116      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25117      */
25118     bubble : function(fn, scope, args){
25119         var p = this;
25120         while(p){
25121             if(fn.call(scope || p, args || p) === false){
25122                 break;
25123             }
25124             p = p.parentNode;
25125         }
25126     },
25127
25128     /**
25129      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25130      * function call will be the scope provided or the current node. The arguments to the function
25131      * will be the args provided or the current node. If the function returns false at any point,
25132      * the cascade is stopped on that branch.
25133      * @param {Function} fn The function to call
25134      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25135      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25136      */
25137     cascade : function(fn, scope, args){
25138         if(fn.call(scope || this, args || this) !== false){
25139             var cs = this.childNodes;
25140             for(var i = 0, len = cs.length; i < len; i++) {
25141                 cs[i].cascade(fn, scope, args);
25142             }
25143         }
25144     },
25145
25146     /**
25147      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25148      * function call will be the scope provided or the current node. The arguments to the function
25149      * will be the args provided or the current node. If the function returns false at any point,
25150      * the iteration stops.
25151      * @param {Function} fn The function to call
25152      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25153      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25154      */
25155     eachChild : function(fn, scope, args){
25156         var cs = this.childNodes;
25157         for(var i = 0, len = cs.length; i < len; i++) {
25158                 if(fn.call(scope || this, args || cs[i]) === false){
25159                     break;
25160                 }
25161         }
25162     },
25163
25164     /**
25165      * Finds the first child that has the attribute with the specified value.
25166      * @param {String} attribute The attribute name
25167      * @param {Mixed} value The value to search for
25168      * @return {Node} The found child or null if none was found
25169      */
25170     findChild : function(attribute, value){
25171         var cs = this.childNodes;
25172         for(var i = 0, len = cs.length; i < len; i++) {
25173                 if(cs[i].attributes[attribute] == value){
25174                     return cs[i];
25175                 }
25176         }
25177         return null;
25178     },
25179
25180     /**
25181      * Finds the first child by a custom function. The child matches if the function passed
25182      * returns true.
25183      * @param {Function} fn
25184      * @param {Object} scope (optional)
25185      * @return {Node} The found child or null if none was found
25186      */
25187     findChildBy : function(fn, scope){
25188         var cs = this.childNodes;
25189         for(var i = 0, len = cs.length; i < len; i++) {
25190                 if(fn.call(scope||cs[i], cs[i]) === true){
25191                     return cs[i];
25192                 }
25193         }
25194         return null;
25195     },
25196
25197     /**
25198      * Sorts this nodes children using the supplied sort function
25199      * @param {Function} fn
25200      * @param {Object} scope (optional)
25201      */
25202     sort : function(fn, scope){
25203         var cs = this.childNodes;
25204         var len = cs.length;
25205         if(len > 0){
25206             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25207             cs.sort(sortFn);
25208             for(var i = 0; i < len; i++){
25209                 var n = cs[i];
25210                 n.previousSibling = cs[i-1];
25211                 n.nextSibling = cs[i+1];
25212                 if(i == 0){
25213                     this.setFirstChild(n);
25214                 }
25215                 if(i == len-1){
25216                     this.setLastChild(n);
25217                 }
25218             }
25219         }
25220     },
25221
25222     /**
25223      * Returns true if this node is an ancestor (at any point) of the passed node.
25224      * @param {Node} node
25225      * @return {Boolean}
25226      */
25227     contains : function(node){
25228         return node.isAncestor(this);
25229     },
25230
25231     /**
25232      * Returns true if the passed node is an ancestor (at any point) of this node.
25233      * @param {Node} node
25234      * @return {Boolean}
25235      */
25236     isAncestor : function(node){
25237         var p = this.parentNode;
25238         while(p){
25239             if(p == node){
25240                 return true;
25241             }
25242             p = p.parentNode;
25243         }
25244         return false;
25245     },
25246
25247     toString : function(){
25248         return "[Node"+(this.id?" "+this.id:"")+"]";
25249     }
25250 });/*
25251  * Based on:
25252  * Ext JS Library 1.1.1
25253  * Copyright(c) 2006-2007, Ext JS, LLC.
25254  *
25255  * Originally Released Under LGPL - original licence link has changed is not relivant.
25256  *
25257  * Fork - LGPL
25258  * <script type="text/javascript">
25259  */
25260  (function(){ 
25261 /**
25262  * @class Roo.Layer
25263  * @extends Roo.Element
25264  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25265  * automatic maintaining of shadow/shim positions.
25266  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25267  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25268  * you can pass a string with a CSS class name. False turns off the shadow.
25269  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25270  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25271  * @cfg {String} cls CSS class to add to the element
25272  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25273  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25274  * @constructor
25275  * @param {Object} config An object with config options.
25276  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25277  */
25278
25279 Roo.Layer = function(config, existingEl){
25280     config = config || {};
25281     var dh = Roo.DomHelper;
25282     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25283     if(existingEl){
25284         this.dom = Roo.getDom(existingEl);
25285     }
25286     if(!this.dom){
25287         var o = config.dh || {tag: "div", cls: "x-layer"};
25288         this.dom = dh.append(pel, o);
25289     }
25290     if(config.cls){
25291         this.addClass(config.cls);
25292     }
25293     this.constrain = config.constrain !== false;
25294     this.visibilityMode = Roo.Element.VISIBILITY;
25295     if(config.id){
25296         this.id = this.dom.id = config.id;
25297     }else{
25298         this.id = Roo.id(this.dom);
25299     }
25300     this.zindex = config.zindex || this.getZIndex();
25301     this.position("absolute", this.zindex);
25302     if(config.shadow){
25303         this.shadowOffset = config.shadowOffset || 4;
25304         this.shadow = new Roo.Shadow({
25305             offset : this.shadowOffset,
25306             mode : config.shadow
25307         });
25308     }else{
25309         this.shadowOffset = 0;
25310     }
25311     this.useShim = config.shim !== false && Roo.useShims;
25312     this.useDisplay = config.useDisplay;
25313     this.hide();
25314 };
25315
25316 var supr = Roo.Element.prototype;
25317
25318 // shims are shared among layer to keep from having 100 iframes
25319 var shims = [];
25320
25321 Roo.extend(Roo.Layer, Roo.Element, {
25322
25323     getZIndex : function(){
25324         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25325     },
25326
25327     getShim : function(){
25328         if(!this.useShim){
25329             return null;
25330         }
25331         if(this.shim){
25332             return this.shim;
25333         }
25334         var shim = shims.shift();
25335         if(!shim){
25336             shim = this.createShim();
25337             shim.enableDisplayMode('block');
25338             shim.dom.style.display = 'none';
25339             shim.dom.style.visibility = 'visible';
25340         }
25341         var pn = this.dom.parentNode;
25342         if(shim.dom.parentNode != pn){
25343             pn.insertBefore(shim.dom, this.dom);
25344         }
25345         shim.setStyle('z-index', this.getZIndex()-2);
25346         this.shim = shim;
25347         return shim;
25348     },
25349
25350     hideShim : function(){
25351         if(this.shim){
25352             this.shim.setDisplayed(false);
25353             shims.push(this.shim);
25354             delete this.shim;
25355         }
25356     },
25357
25358     disableShadow : function(){
25359         if(this.shadow){
25360             this.shadowDisabled = true;
25361             this.shadow.hide();
25362             this.lastShadowOffset = this.shadowOffset;
25363             this.shadowOffset = 0;
25364         }
25365     },
25366
25367     enableShadow : function(show){
25368         if(this.shadow){
25369             this.shadowDisabled = false;
25370             this.shadowOffset = this.lastShadowOffset;
25371             delete this.lastShadowOffset;
25372             if(show){
25373                 this.sync(true);
25374             }
25375         }
25376     },
25377
25378     // private
25379     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25380     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25381     sync : function(doShow){
25382         var sw = this.shadow;
25383         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25384             var sh = this.getShim();
25385
25386             var w = this.getWidth(),
25387                 h = this.getHeight();
25388
25389             var l = this.getLeft(true),
25390                 t = this.getTop(true);
25391
25392             if(sw && !this.shadowDisabled){
25393                 if(doShow && !sw.isVisible()){
25394                     sw.show(this);
25395                 }else{
25396                     sw.realign(l, t, w, h);
25397                 }
25398                 if(sh){
25399                     if(doShow){
25400                        sh.show();
25401                     }
25402                     // fit the shim behind the shadow, so it is shimmed too
25403                     var a = sw.adjusts, s = sh.dom.style;
25404                     s.left = (Math.min(l, l+a.l))+"px";
25405                     s.top = (Math.min(t, t+a.t))+"px";
25406                     s.width = (w+a.w)+"px";
25407                     s.height = (h+a.h)+"px";
25408                 }
25409             }else if(sh){
25410                 if(doShow){
25411                    sh.show();
25412                 }
25413                 sh.setSize(w, h);
25414                 sh.setLeftTop(l, t);
25415             }
25416             
25417         }
25418     },
25419
25420     // private
25421     destroy : function(){
25422         this.hideShim();
25423         if(this.shadow){
25424             this.shadow.hide();
25425         }
25426         this.removeAllListeners();
25427         var pn = this.dom.parentNode;
25428         if(pn){
25429             pn.removeChild(this.dom);
25430         }
25431         Roo.Element.uncache(this.id);
25432     },
25433
25434     remove : function(){
25435         this.destroy();
25436     },
25437
25438     // private
25439     beginUpdate : function(){
25440         this.updating = true;
25441     },
25442
25443     // private
25444     endUpdate : function(){
25445         this.updating = false;
25446         this.sync(true);
25447     },
25448
25449     // private
25450     hideUnders : function(negOffset){
25451         if(this.shadow){
25452             this.shadow.hide();
25453         }
25454         this.hideShim();
25455     },
25456
25457     // private
25458     constrainXY : function(){
25459         if(this.constrain){
25460             var vw = Roo.lib.Dom.getViewWidth(),
25461                 vh = Roo.lib.Dom.getViewHeight();
25462             var s = Roo.get(document).getScroll();
25463
25464             var xy = this.getXY();
25465             var x = xy[0], y = xy[1];   
25466             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25467             // only move it if it needs it
25468             var moved = false;
25469             // first validate right/bottom
25470             if((x + w) > vw+s.left){
25471                 x = vw - w - this.shadowOffset;
25472                 moved = true;
25473             }
25474             if((y + h) > vh+s.top){
25475                 y = vh - h - this.shadowOffset;
25476                 moved = true;
25477             }
25478             // then make sure top/left isn't negative
25479             if(x < s.left){
25480                 x = s.left;
25481                 moved = true;
25482             }
25483             if(y < s.top){
25484                 y = s.top;
25485                 moved = true;
25486             }
25487             if(moved){
25488                 if(this.avoidY){
25489                     var ay = this.avoidY;
25490                     if(y <= ay && (y+h) >= ay){
25491                         y = ay-h-5;   
25492                     }
25493                 }
25494                 xy = [x, y];
25495                 this.storeXY(xy);
25496                 supr.setXY.call(this, xy);
25497                 this.sync();
25498             }
25499         }
25500     },
25501
25502     isVisible : function(){
25503         return this.visible;    
25504     },
25505
25506     // private
25507     showAction : function(){
25508         this.visible = true; // track visibility to prevent getStyle calls
25509         if(this.useDisplay === true){
25510             this.setDisplayed("");
25511         }else if(this.lastXY){
25512             supr.setXY.call(this, this.lastXY);
25513         }else if(this.lastLT){
25514             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25515         }
25516     },
25517
25518     // private
25519     hideAction : function(){
25520         this.visible = false;
25521         if(this.useDisplay === true){
25522             this.setDisplayed(false);
25523         }else{
25524             this.setLeftTop(-10000,-10000);
25525         }
25526     },
25527
25528     // overridden Element method
25529     setVisible : function(v, a, d, c, e){
25530         if(v){
25531             this.showAction();
25532         }
25533         if(a && v){
25534             var cb = function(){
25535                 this.sync(true);
25536                 if(c){
25537                     c();
25538                 }
25539             }.createDelegate(this);
25540             supr.setVisible.call(this, true, true, d, cb, e);
25541         }else{
25542             if(!v){
25543                 this.hideUnders(true);
25544             }
25545             var cb = c;
25546             if(a){
25547                 cb = function(){
25548                     this.hideAction();
25549                     if(c){
25550                         c();
25551                     }
25552                 }.createDelegate(this);
25553             }
25554             supr.setVisible.call(this, v, a, d, cb, e);
25555             if(v){
25556                 this.sync(true);
25557             }else if(!a){
25558                 this.hideAction();
25559             }
25560         }
25561     },
25562
25563     storeXY : function(xy){
25564         delete this.lastLT;
25565         this.lastXY = xy;
25566     },
25567
25568     storeLeftTop : function(left, top){
25569         delete this.lastXY;
25570         this.lastLT = [left, top];
25571     },
25572
25573     // private
25574     beforeFx : function(){
25575         this.beforeAction();
25576         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25577     },
25578
25579     // private
25580     afterFx : function(){
25581         Roo.Layer.superclass.afterFx.apply(this, arguments);
25582         this.sync(this.isVisible());
25583     },
25584
25585     // private
25586     beforeAction : function(){
25587         if(!this.updating && this.shadow){
25588             this.shadow.hide();
25589         }
25590     },
25591
25592     // overridden Element method
25593     setLeft : function(left){
25594         this.storeLeftTop(left, this.getTop(true));
25595         supr.setLeft.apply(this, arguments);
25596         this.sync();
25597     },
25598
25599     setTop : function(top){
25600         this.storeLeftTop(this.getLeft(true), top);
25601         supr.setTop.apply(this, arguments);
25602         this.sync();
25603     },
25604
25605     setLeftTop : function(left, top){
25606         this.storeLeftTop(left, top);
25607         supr.setLeftTop.apply(this, arguments);
25608         this.sync();
25609     },
25610
25611     setXY : function(xy, a, d, c, e){
25612         this.fixDisplay();
25613         this.beforeAction();
25614         this.storeXY(xy);
25615         var cb = this.createCB(c);
25616         supr.setXY.call(this, xy, a, d, cb, e);
25617         if(!a){
25618             cb();
25619         }
25620     },
25621
25622     // private
25623     createCB : function(c){
25624         var el = this;
25625         return function(){
25626             el.constrainXY();
25627             el.sync(true);
25628             if(c){
25629                 c();
25630             }
25631         };
25632     },
25633
25634     // overridden Element method
25635     setX : function(x, a, d, c, e){
25636         this.setXY([x, this.getY()], a, d, c, e);
25637     },
25638
25639     // overridden Element method
25640     setY : function(y, a, d, c, e){
25641         this.setXY([this.getX(), y], a, d, c, e);
25642     },
25643
25644     // overridden Element method
25645     setSize : function(w, h, a, d, c, e){
25646         this.beforeAction();
25647         var cb = this.createCB(c);
25648         supr.setSize.call(this, w, h, a, d, cb, e);
25649         if(!a){
25650             cb();
25651         }
25652     },
25653
25654     // overridden Element method
25655     setWidth : function(w, a, d, c, e){
25656         this.beforeAction();
25657         var cb = this.createCB(c);
25658         supr.setWidth.call(this, w, a, d, cb, e);
25659         if(!a){
25660             cb();
25661         }
25662     },
25663
25664     // overridden Element method
25665     setHeight : function(h, a, d, c, e){
25666         this.beforeAction();
25667         var cb = this.createCB(c);
25668         supr.setHeight.call(this, h, a, d, cb, e);
25669         if(!a){
25670             cb();
25671         }
25672     },
25673
25674     // overridden Element method
25675     setBounds : function(x, y, w, h, a, d, c, e){
25676         this.beforeAction();
25677         var cb = this.createCB(c);
25678         if(!a){
25679             this.storeXY([x, y]);
25680             supr.setXY.call(this, [x, y]);
25681             supr.setSize.call(this, w, h, a, d, cb, e);
25682             cb();
25683         }else{
25684             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25685         }
25686         return this;
25687     },
25688     
25689     /**
25690      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25691      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25692      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25693      * @param {Number} zindex The new z-index to set
25694      * @return {this} The Layer
25695      */
25696     setZIndex : function(zindex){
25697         this.zindex = zindex;
25698         this.setStyle("z-index", zindex + 2);
25699         if(this.shadow){
25700             this.shadow.setZIndex(zindex + 1);
25701         }
25702         if(this.shim){
25703             this.shim.setStyle("z-index", zindex);
25704         }
25705     }
25706 });
25707 })();/*
25708  * Based on:
25709  * Ext JS Library 1.1.1
25710  * Copyright(c) 2006-2007, Ext JS, LLC.
25711  *
25712  * Originally Released Under LGPL - original licence link has changed is not relivant.
25713  *
25714  * Fork - LGPL
25715  * <script type="text/javascript">
25716  */
25717
25718
25719 /**
25720  * @class Roo.Shadow
25721  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25722  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25723  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25724  * @constructor
25725  * Create a new Shadow
25726  * @param {Object} config The config object
25727  */
25728 Roo.Shadow = function(config){
25729     Roo.apply(this, config);
25730     if(typeof this.mode != "string"){
25731         this.mode = this.defaultMode;
25732     }
25733     var o = this.offset, a = {h: 0};
25734     var rad = Math.floor(this.offset/2);
25735     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25736         case "drop":
25737             a.w = 0;
25738             a.l = a.t = o;
25739             a.t -= 1;
25740             if(Roo.isIE){
25741                 a.l -= this.offset + rad;
25742                 a.t -= this.offset + rad;
25743                 a.w -= rad;
25744                 a.h -= rad;
25745                 a.t += 1;
25746             }
25747         break;
25748         case "sides":
25749             a.w = (o*2);
25750             a.l = -o;
25751             a.t = o-1;
25752             if(Roo.isIE){
25753                 a.l -= (this.offset - rad);
25754                 a.t -= this.offset + rad;
25755                 a.l += 1;
25756                 a.w -= (this.offset - rad)*2;
25757                 a.w -= rad + 1;
25758                 a.h -= 1;
25759             }
25760         break;
25761         case "frame":
25762             a.w = a.h = (o*2);
25763             a.l = a.t = -o;
25764             a.t += 1;
25765             a.h -= 2;
25766             if(Roo.isIE){
25767                 a.l -= (this.offset - rad);
25768                 a.t -= (this.offset - rad);
25769                 a.l += 1;
25770                 a.w -= (this.offset + rad + 1);
25771                 a.h -= (this.offset + rad);
25772                 a.h += 1;
25773             }
25774         break;
25775     };
25776
25777     this.adjusts = a;
25778 };
25779
25780 Roo.Shadow.prototype = {
25781     /**
25782      * @cfg {String} mode
25783      * The shadow display mode.  Supports the following options:<br />
25784      * sides: Shadow displays on both sides and bottom only<br />
25785      * frame: Shadow displays equally on all four sides<br />
25786      * drop: Traditional bottom-right drop shadow (default)
25787      */
25788     /**
25789      * @cfg {String} offset
25790      * The number of pixels to offset the shadow from the element (defaults to 4)
25791      */
25792     offset: 4,
25793
25794     // private
25795     defaultMode: "drop",
25796
25797     /**
25798      * Displays the shadow under the target element
25799      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25800      */
25801     show : function(target){
25802         target = Roo.get(target);
25803         if(!this.el){
25804             this.el = Roo.Shadow.Pool.pull();
25805             if(this.el.dom.nextSibling != target.dom){
25806                 this.el.insertBefore(target);
25807             }
25808         }
25809         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25810         if(Roo.isIE){
25811             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25812         }
25813         this.realign(
25814             target.getLeft(true),
25815             target.getTop(true),
25816             target.getWidth(),
25817             target.getHeight()
25818         );
25819         this.el.dom.style.display = "block";
25820     },
25821
25822     /**
25823      * Returns true if the shadow is visible, else false
25824      */
25825     isVisible : function(){
25826         return this.el ? true : false;  
25827     },
25828
25829     /**
25830      * Direct alignment when values are already available. Show must be called at least once before
25831      * calling this method to ensure it is initialized.
25832      * @param {Number} left The target element left position
25833      * @param {Number} top The target element top position
25834      * @param {Number} width The target element width
25835      * @param {Number} height The target element height
25836      */
25837     realign : function(l, t, w, h){
25838         if(!this.el){
25839             return;
25840         }
25841         var a = this.adjusts, d = this.el.dom, s = d.style;
25842         var iea = 0;
25843         s.left = (l+a.l)+"px";
25844         s.top = (t+a.t)+"px";
25845         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25846  
25847         if(s.width != sws || s.height != shs){
25848             s.width = sws;
25849             s.height = shs;
25850             if(!Roo.isIE){
25851                 var cn = d.childNodes;
25852                 var sww = Math.max(0, (sw-12))+"px";
25853                 cn[0].childNodes[1].style.width = sww;
25854                 cn[1].childNodes[1].style.width = sww;
25855                 cn[2].childNodes[1].style.width = sww;
25856                 cn[1].style.height = Math.max(0, (sh-12))+"px";
25857             }
25858         }
25859     },
25860
25861     /**
25862      * Hides this shadow
25863      */
25864     hide : function(){
25865         if(this.el){
25866             this.el.dom.style.display = "none";
25867             Roo.Shadow.Pool.push(this.el);
25868             delete this.el;
25869         }
25870     },
25871
25872     /**
25873      * Adjust the z-index of this shadow
25874      * @param {Number} zindex The new z-index
25875      */
25876     setZIndex : function(z){
25877         this.zIndex = z;
25878         if(this.el){
25879             this.el.setStyle("z-index", z);
25880         }
25881     }
25882 };
25883
25884 // Private utility class that manages the internal Shadow cache
25885 Roo.Shadow.Pool = function(){
25886     var p = [];
25887     var markup = Roo.isIE ?
25888                  '<div class="x-ie-shadow"></div>' :
25889                  '<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>';
25890     return {
25891         pull : function(){
25892             var sh = p.shift();
25893             if(!sh){
25894                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
25895                 sh.autoBoxAdjust = false;
25896             }
25897             return sh;
25898         },
25899
25900         push : function(sh){
25901             p.push(sh);
25902         }
25903     };
25904 }();/*
25905  * Based on:
25906  * Ext JS Library 1.1.1
25907  * Copyright(c) 2006-2007, Ext JS, LLC.
25908  *
25909  * Originally Released Under LGPL - original licence link has changed is not relivant.
25910  *
25911  * Fork - LGPL
25912  * <script type="text/javascript">
25913  */
25914
25915
25916 /**
25917  * @class Roo.SplitBar
25918  * @extends Roo.util.Observable
25919  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
25920  * <br><br>
25921  * Usage:
25922  * <pre><code>
25923 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
25924                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
25925 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
25926 split.minSize = 100;
25927 split.maxSize = 600;
25928 split.animate = true;
25929 split.on('moved', splitterMoved);
25930 </code></pre>
25931  * @constructor
25932  * Create a new SplitBar
25933  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
25934  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
25935  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25936  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
25937                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
25938                         position of the SplitBar).
25939  */
25940 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
25941     
25942     /** @private */
25943     this.el = Roo.get(dragElement, true);
25944     this.el.dom.unselectable = "on";
25945     /** @private */
25946     this.resizingEl = Roo.get(resizingElement, true);
25947
25948     /**
25949      * @private
25950      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
25951      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
25952      * @type Number
25953      */
25954     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
25955     
25956     /**
25957      * The minimum size of the resizing element. (Defaults to 0)
25958      * @type Number
25959      */
25960     this.minSize = 0;
25961     
25962     /**
25963      * The maximum size of the resizing element. (Defaults to 2000)
25964      * @type Number
25965      */
25966     this.maxSize = 2000;
25967     
25968     /**
25969      * Whether to animate the transition to the new size
25970      * @type Boolean
25971      */
25972     this.animate = false;
25973     
25974     /**
25975      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
25976      * @type Boolean
25977      */
25978     this.useShim = false;
25979     
25980     /** @private */
25981     this.shim = null;
25982     
25983     if(!existingProxy){
25984         /** @private */
25985         this.proxy = Roo.SplitBar.createProxy(this.orientation);
25986     }else{
25987         this.proxy = Roo.get(existingProxy).dom;
25988     }
25989     /** @private */
25990     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
25991     
25992     /** @private */
25993     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
25994     
25995     /** @private */
25996     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
25997     
25998     /** @private */
25999     this.dragSpecs = {};
26000     
26001     /**
26002      * @private The adapter to use to positon and resize elements
26003      */
26004     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26005     this.adapter.init(this);
26006     
26007     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26008         /** @private */
26009         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26010         this.el.addClass("x-splitbar-h");
26011     }else{
26012         /** @private */
26013         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26014         this.el.addClass("x-splitbar-v");
26015     }
26016     
26017     this.addEvents({
26018         /**
26019          * @event resize
26020          * Fires when the splitter is moved (alias for {@link #event-moved})
26021          * @param {Roo.SplitBar} this
26022          * @param {Number} newSize the new width or height
26023          */
26024         "resize" : true,
26025         /**
26026          * @event moved
26027          * Fires when the splitter is moved
26028          * @param {Roo.SplitBar} this
26029          * @param {Number} newSize the new width or height
26030          */
26031         "moved" : true,
26032         /**
26033          * @event beforeresize
26034          * Fires before the splitter is dragged
26035          * @param {Roo.SplitBar} this
26036          */
26037         "beforeresize" : true,
26038
26039         "beforeapply" : true
26040     });
26041
26042     Roo.util.Observable.call(this);
26043 };
26044
26045 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26046     onStartProxyDrag : function(x, y){
26047         this.fireEvent("beforeresize", this);
26048         if(!this.overlay){
26049             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26050             o.unselectable();
26051             o.enableDisplayMode("block");
26052             // all splitbars share the same overlay
26053             Roo.SplitBar.prototype.overlay = o;
26054         }
26055         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26056         this.overlay.show();
26057         Roo.get(this.proxy).setDisplayed("block");
26058         var size = this.adapter.getElementSize(this);
26059         this.activeMinSize = this.getMinimumSize();;
26060         this.activeMaxSize = this.getMaximumSize();;
26061         var c1 = size - this.activeMinSize;
26062         var c2 = Math.max(this.activeMaxSize - size, 0);
26063         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26064             this.dd.resetConstraints();
26065             this.dd.setXConstraint(
26066                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26067                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26068             );
26069             this.dd.setYConstraint(0, 0);
26070         }else{
26071             this.dd.resetConstraints();
26072             this.dd.setXConstraint(0, 0);
26073             this.dd.setYConstraint(
26074                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26075                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26076             );
26077          }
26078         this.dragSpecs.startSize = size;
26079         this.dragSpecs.startPoint = [x, y];
26080         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26081     },
26082     
26083     /** 
26084      * @private Called after the drag operation by the DDProxy
26085      */
26086     onEndProxyDrag : function(e){
26087         Roo.get(this.proxy).setDisplayed(false);
26088         var endPoint = Roo.lib.Event.getXY(e);
26089         if(this.overlay){
26090             this.overlay.hide();
26091         }
26092         var newSize;
26093         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26094             newSize = this.dragSpecs.startSize + 
26095                 (this.placement == Roo.SplitBar.LEFT ?
26096                     endPoint[0] - this.dragSpecs.startPoint[0] :
26097                     this.dragSpecs.startPoint[0] - endPoint[0]
26098                 );
26099         }else{
26100             newSize = this.dragSpecs.startSize + 
26101                 (this.placement == Roo.SplitBar.TOP ?
26102                     endPoint[1] - this.dragSpecs.startPoint[1] :
26103                     this.dragSpecs.startPoint[1] - endPoint[1]
26104                 );
26105         }
26106         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26107         if(newSize != this.dragSpecs.startSize){
26108             if(this.fireEvent('beforeapply', this, newSize) !== false){
26109                 this.adapter.setElementSize(this, newSize);
26110                 this.fireEvent("moved", this, newSize);
26111                 this.fireEvent("resize", this, newSize);
26112             }
26113         }
26114     },
26115     
26116     /**
26117      * Get the adapter this SplitBar uses
26118      * @return The adapter object
26119      */
26120     getAdapter : function(){
26121         return this.adapter;
26122     },
26123     
26124     /**
26125      * Set the adapter this SplitBar uses
26126      * @param {Object} adapter A SplitBar adapter object
26127      */
26128     setAdapter : function(adapter){
26129         this.adapter = adapter;
26130         this.adapter.init(this);
26131     },
26132     
26133     /**
26134      * Gets the minimum size for the resizing element
26135      * @return {Number} The minimum size
26136      */
26137     getMinimumSize : function(){
26138         return this.minSize;
26139     },
26140     
26141     /**
26142      * Sets the minimum size for the resizing element
26143      * @param {Number} minSize The minimum size
26144      */
26145     setMinimumSize : function(minSize){
26146         this.minSize = minSize;
26147     },
26148     
26149     /**
26150      * Gets the maximum size for the resizing element
26151      * @return {Number} The maximum size
26152      */
26153     getMaximumSize : function(){
26154         return this.maxSize;
26155     },
26156     
26157     /**
26158      * Sets the maximum size for the resizing element
26159      * @param {Number} maxSize The maximum size
26160      */
26161     setMaximumSize : function(maxSize){
26162         this.maxSize = maxSize;
26163     },
26164     
26165     /**
26166      * Sets the initialize size for the resizing element
26167      * @param {Number} size The initial size
26168      */
26169     setCurrentSize : function(size){
26170         var oldAnimate = this.animate;
26171         this.animate = false;
26172         this.adapter.setElementSize(this, size);
26173         this.animate = oldAnimate;
26174     },
26175     
26176     /**
26177      * Destroy this splitbar. 
26178      * @param {Boolean} removeEl True to remove the element
26179      */
26180     destroy : function(removeEl){
26181         if(this.shim){
26182             this.shim.remove();
26183         }
26184         this.dd.unreg();
26185         this.proxy.parentNode.removeChild(this.proxy);
26186         if(removeEl){
26187             this.el.remove();
26188         }
26189     }
26190 });
26191
26192 /**
26193  * @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.
26194  */
26195 Roo.SplitBar.createProxy = function(dir){
26196     var proxy = new Roo.Element(document.createElement("div"));
26197     proxy.unselectable();
26198     var cls = 'x-splitbar-proxy';
26199     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26200     document.body.appendChild(proxy.dom);
26201     return proxy.dom;
26202 };
26203
26204 /** 
26205  * @class Roo.SplitBar.BasicLayoutAdapter
26206  * Default Adapter. It assumes the splitter and resizing element are not positioned
26207  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26208  */
26209 Roo.SplitBar.BasicLayoutAdapter = function(){
26210 };
26211
26212 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26213     // do nothing for now
26214     init : function(s){
26215     
26216     },
26217     /**
26218      * Called before drag operations to get the current size of the resizing element. 
26219      * @param {Roo.SplitBar} s The SplitBar using this adapter
26220      */
26221      getElementSize : function(s){
26222         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26223             return s.resizingEl.getWidth();
26224         }else{
26225             return s.resizingEl.getHeight();
26226         }
26227     },
26228     
26229     /**
26230      * Called after drag operations to set the size of the resizing element.
26231      * @param {Roo.SplitBar} s The SplitBar using this adapter
26232      * @param {Number} newSize The new size to set
26233      * @param {Function} onComplete A function to be invoked when resizing is complete
26234      */
26235     setElementSize : function(s, newSize, onComplete){
26236         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26237             if(!s.animate){
26238                 s.resizingEl.setWidth(newSize);
26239                 if(onComplete){
26240                     onComplete(s, newSize);
26241                 }
26242             }else{
26243                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26244             }
26245         }else{
26246             
26247             if(!s.animate){
26248                 s.resizingEl.setHeight(newSize);
26249                 if(onComplete){
26250                     onComplete(s, newSize);
26251                 }
26252             }else{
26253                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26254             }
26255         }
26256     }
26257 };
26258
26259 /** 
26260  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26261  * @extends Roo.SplitBar.BasicLayoutAdapter
26262  * Adapter that  moves the splitter element to align with the resized sizing element. 
26263  * Used with an absolute positioned SplitBar.
26264  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26265  * document.body, make sure you assign an id to the body element.
26266  */
26267 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26268     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26269     this.container = Roo.get(container);
26270 };
26271
26272 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26273     init : function(s){
26274         this.basic.init(s);
26275     },
26276     
26277     getElementSize : function(s){
26278         return this.basic.getElementSize(s);
26279     },
26280     
26281     setElementSize : function(s, newSize, onComplete){
26282         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26283     },
26284     
26285     moveSplitter : function(s){
26286         var yes = Roo.SplitBar;
26287         switch(s.placement){
26288             case yes.LEFT:
26289                 s.el.setX(s.resizingEl.getRight());
26290                 break;
26291             case yes.RIGHT:
26292                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26293                 break;
26294             case yes.TOP:
26295                 s.el.setY(s.resizingEl.getBottom());
26296                 break;
26297             case yes.BOTTOM:
26298                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26299                 break;
26300         }
26301     }
26302 };
26303
26304 /**
26305  * Orientation constant - Create a vertical SplitBar
26306  * @static
26307  * @type Number
26308  */
26309 Roo.SplitBar.VERTICAL = 1;
26310
26311 /**
26312  * Orientation constant - Create a horizontal SplitBar
26313  * @static
26314  * @type Number
26315  */
26316 Roo.SplitBar.HORIZONTAL = 2;
26317
26318 /**
26319  * Placement constant - The resizing element is to the left of the splitter element
26320  * @static
26321  * @type Number
26322  */
26323 Roo.SplitBar.LEFT = 1;
26324
26325 /**
26326  * Placement constant - The resizing element is to the right of the splitter element
26327  * @static
26328  * @type Number
26329  */
26330 Roo.SplitBar.RIGHT = 2;
26331
26332 /**
26333  * Placement constant - The resizing element is positioned above the splitter element
26334  * @static
26335  * @type Number
26336  */
26337 Roo.SplitBar.TOP = 3;
26338
26339 /**
26340  * Placement constant - The resizing element is positioned under splitter element
26341  * @static
26342  * @type Number
26343  */
26344 Roo.SplitBar.BOTTOM = 4;
26345 /*
26346  * Based on:
26347  * Ext JS Library 1.1.1
26348  * Copyright(c) 2006-2007, Ext JS, LLC.
26349  *
26350  * Originally Released Under LGPL - original licence link has changed is not relivant.
26351  *
26352  * Fork - LGPL
26353  * <script type="text/javascript">
26354  */
26355
26356 /**
26357  * @class Roo.View
26358  * @extends Roo.util.Observable
26359  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26360  * This class also supports single and multi selection modes. <br>
26361  * Create a data model bound view:
26362  <pre><code>
26363  var store = new Roo.data.Store(...);
26364
26365  var view = new Roo.View({
26366     el : "my-element",
26367     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26368  
26369     singleSelect: true,
26370     selectedClass: "ydataview-selected",
26371     store: store
26372  });
26373
26374  // listen for node click?
26375  view.on("click", function(vw, index, node, e){
26376  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26377  });
26378
26379  // load XML data
26380  dataModel.load("foobar.xml");
26381  </code></pre>
26382  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26383  * <br><br>
26384  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26385  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26386  * 
26387  * Note: old style constructor is still suported (container, template, config)
26388  * 
26389  * @constructor
26390  * Create a new View
26391  * @param {Object} config The config object
26392  * 
26393  */
26394 Roo.View = function(config, depreciated_tpl, depreciated_config){
26395     
26396     this.parent = false;
26397     
26398     if (typeof(depreciated_tpl) == 'undefined') {
26399         // new way.. - universal constructor.
26400         Roo.apply(this, config);
26401         this.el  = Roo.get(this.el);
26402     } else {
26403         // old format..
26404         this.el  = Roo.get(config);
26405         this.tpl = depreciated_tpl;
26406         Roo.apply(this, depreciated_config);
26407     }
26408     this.wrapEl  = this.el.wrap().wrap();
26409     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26410     
26411     
26412     if(typeof(this.tpl) == "string"){
26413         this.tpl = new Roo.Template(this.tpl);
26414     } else {
26415         // support xtype ctors..
26416         this.tpl = new Roo.factory(this.tpl, Roo);
26417     }
26418     
26419     
26420     this.tpl.compile();
26421     
26422     /** @private */
26423     this.addEvents({
26424         /**
26425          * @event beforeclick
26426          * Fires before a click is processed. Returns false to cancel the default action.
26427          * @param {Roo.View} this
26428          * @param {Number} index The index of the target node
26429          * @param {HTMLElement} node The target node
26430          * @param {Roo.EventObject} e The raw event object
26431          */
26432             "beforeclick" : true,
26433         /**
26434          * @event click
26435          * Fires when a template node is clicked.
26436          * @param {Roo.View} this
26437          * @param {Number} index The index of the target node
26438          * @param {HTMLElement} node The target node
26439          * @param {Roo.EventObject} e The raw event object
26440          */
26441             "click" : true,
26442         /**
26443          * @event dblclick
26444          * Fires when a template node is double clicked.
26445          * @param {Roo.View} this
26446          * @param {Number} index The index of the target node
26447          * @param {HTMLElement} node The target node
26448          * @param {Roo.EventObject} e The raw event object
26449          */
26450             "dblclick" : true,
26451         /**
26452          * @event contextmenu
26453          * Fires when a template node is right clicked.
26454          * @param {Roo.View} this
26455          * @param {Number} index The index of the target node
26456          * @param {HTMLElement} node The target node
26457          * @param {Roo.EventObject} e The raw event object
26458          */
26459             "contextmenu" : true,
26460         /**
26461          * @event selectionchange
26462          * Fires when the selected nodes change.
26463          * @param {Roo.View} this
26464          * @param {Array} selections Array of the selected nodes
26465          */
26466             "selectionchange" : true,
26467     
26468         /**
26469          * @event beforeselect
26470          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26471          * @param {Roo.View} this
26472          * @param {HTMLElement} node The node to be selected
26473          * @param {Array} selections Array of currently selected nodes
26474          */
26475             "beforeselect" : true,
26476         /**
26477          * @event preparedata
26478          * Fires on every row to render, to allow you to change the data.
26479          * @param {Roo.View} this
26480          * @param {Object} data to be rendered (change this)
26481          */
26482           "preparedata" : true
26483           
26484           
26485         });
26486
26487
26488
26489     this.el.on({
26490         "click": this.onClick,
26491         "dblclick": this.onDblClick,
26492         "contextmenu": this.onContextMenu,
26493         scope:this
26494     });
26495
26496     this.selections = [];
26497     this.nodes = [];
26498     this.cmp = new Roo.CompositeElementLite([]);
26499     if(this.store){
26500         this.store = Roo.factory(this.store, Roo.data);
26501         this.setStore(this.store, true);
26502     }
26503     
26504     if ( this.footer && this.footer.xtype) {
26505            
26506          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26507         
26508         this.footer.dataSource = this.store;
26509         this.footer.container = fctr;
26510         this.footer = Roo.factory(this.footer, Roo);
26511         fctr.insertFirst(this.el);
26512         
26513         // this is a bit insane - as the paging toolbar seems to detach the el..
26514 //        dom.parentNode.parentNode.parentNode
26515          // they get detached?
26516     }
26517     
26518     
26519     Roo.View.superclass.constructor.call(this);
26520     
26521     
26522 };
26523
26524 Roo.extend(Roo.View, Roo.util.Observable, {
26525     
26526      /**
26527      * @cfg {Roo.data.Store} store Data store to load data from.
26528      */
26529     store : false,
26530     
26531     /**
26532      * @cfg {String|Roo.Element} el The container element.
26533      */
26534     el : '',
26535     
26536     /**
26537      * @cfg {String|Roo.Template} tpl The template used by this View 
26538      */
26539     tpl : false,
26540     /**
26541      * @cfg {String} dataName the named area of the template to use as the data area
26542      *                          Works with domtemplates roo-name="name"
26543      */
26544     dataName: false,
26545     /**
26546      * @cfg {String} selectedClass The css class to add to selected nodes
26547      */
26548     selectedClass : "x-view-selected",
26549      /**
26550      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26551      */
26552     emptyText : "",
26553     
26554     /**
26555      * @cfg {String} text to display on mask (default Loading)
26556      */
26557     mask : false,
26558     /**
26559      * @cfg {Boolean} multiSelect Allow multiple selection
26560      */
26561     multiSelect : false,
26562     /**
26563      * @cfg {Boolean} singleSelect Allow single selection
26564      */
26565     singleSelect:  false,
26566     
26567     /**
26568      * @cfg {Boolean} toggleSelect - selecting 
26569      */
26570     toggleSelect : false,
26571     
26572     /**
26573      * @cfg {Boolean} tickable - selecting 
26574      */
26575     tickable : false,
26576     
26577     /**
26578      * Returns the element this view is bound to.
26579      * @return {Roo.Element}
26580      */
26581     getEl : function(){
26582         return this.wrapEl;
26583     },
26584     
26585     
26586
26587     /**
26588      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26589      */
26590     refresh : function(){
26591         //Roo.log('refresh');
26592         var t = this.tpl;
26593         
26594         // if we are using something like 'domtemplate', then
26595         // the what gets used is:
26596         // t.applySubtemplate(NAME, data, wrapping data..)
26597         // the outer template then get' applied with
26598         //     the store 'extra data'
26599         // and the body get's added to the
26600         //      roo-name="data" node?
26601         //      <span class='roo-tpl-{name}'></span> ?????
26602         
26603         
26604         
26605         this.clearSelections();
26606         this.el.update("");
26607         var html = [];
26608         var records = this.store.getRange();
26609         if(records.length < 1) {
26610             
26611             // is this valid??  = should it render a template??
26612             
26613             this.el.update(this.emptyText);
26614             return;
26615         }
26616         var el = this.el;
26617         if (this.dataName) {
26618             this.el.update(t.apply(this.store.meta)); //????
26619             el = this.el.child('.roo-tpl-' + this.dataName);
26620         }
26621         
26622         for(var i = 0, len = records.length; i < len; i++){
26623             var data = this.prepareData(records[i].data, i, records[i]);
26624             this.fireEvent("preparedata", this, data, i, records[i]);
26625             
26626             var d = Roo.apply({}, data);
26627             
26628             if(this.tickable){
26629                 Roo.apply(d, {'roo-id' : Roo.id()});
26630                 
26631                 var _this = this;
26632             
26633                 Roo.each(this.parent.item, function(item){
26634                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26635                         return;
26636                     }
26637                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26638                 });
26639             }
26640             
26641             html[html.length] = Roo.util.Format.trim(
26642                 this.dataName ?
26643                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26644                     t.apply(d)
26645             );
26646         }
26647         
26648         
26649         
26650         el.update(html.join(""));
26651         this.nodes = el.dom.childNodes;
26652         this.updateIndexes(0);
26653     },
26654     
26655
26656     /**
26657      * Function to override to reformat the data that is sent to
26658      * the template for each node.
26659      * DEPRICATED - use the preparedata event handler.
26660      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26661      * a JSON object for an UpdateManager bound view).
26662      */
26663     prepareData : function(data, index, record)
26664     {
26665         this.fireEvent("preparedata", this, data, index, record);
26666         return data;
26667     },
26668
26669     onUpdate : function(ds, record){
26670         // Roo.log('on update');   
26671         this.clearSelections();
26672         var index = this.store.indexOf(record);
26673         var n = this.nodes[index];
26674         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26675         n.parentNode.removeChild(n);
26676         this.updateIndexes(index, index);
26677     },
26678
26679     
26680     
26681 // --------- FIXME     
26682     onAdd : function(ds, records, index)
26683     {
26684         //Roo.log(['on Add', ds, records, index] );        
26685         this.clearSelections();
26686         if(this.nodes.length == 0){
26687             this.refresh();
26688             return;
26689         }
26690         var n = this.nodes[index];
26691         for(var i = 0, len = records.length; i < len; i++){
26692             var d = this.prepareData(records[i].data, i, records[i]);
26693             if(n){
26694                 this.tpl.insertBefore(n, d);
26695             }else{
26696                 
26697                 this.tpl.append(this.el, d);
26698             }
26699         }
26700         this.updateIndexes(index);
26701     },
26702
26703     onRemove : function(ds, record, index){
26704        // Roo.log('onRemove');
26705         this.clearSelections();
26706         var el = this.dataName  ?
26707             this.el.child('.roo-tpl-' + this.dataName) :
26708             this.el; 
26709         
26710         el.dom.removeChild(this.nodes[index]);
26711         this.updateIndexes(index);
26712     },
26713
26714     /**
26715      * Refresh an individual node.
26716      * @param {Number} index
26717      */
26718     refreshNode : function(index){
26719         this.onUpdate(this.store, this.store.getAt(index));
26720     },
26721
26722     updateIndexes : function(startIndex, endIndex){
26723         var ns = this.nodes;
26724         startIndex = startIndex || 0;
26725         endIndex = endIndex || ns.length - 1;
26726         for(var i = startIndex; i <= endIndex; i++){
26727             ns[i].nodeIndex = i;
26728         }
26729     },
26730
26731     /**
26732      * Changes the data store this view uses and refresh the view.
26733      * @param {Store} store
26734      */
26735     setStore : function(store, initial){
26736         if(!initial && this.store){
26737             this.store.un("datachanged", this.refresh);
26738             this.store.un("add", this.onAdd);
26739             this.store.un("remove", this.onRemove);
26740             this.store.un("update", this.onUpdate);
26741             this.store.un("clear", this.refresh);
26742             this.store.un("beforeload", this.onBeforeLoad);
26743             this.store.un("load", this.onLoad);
26744             this.store.un("loadexception", this.onLoad);
26745         }
26746         if(store){
26747           
26748             store.on("datachanged", this.refresh, this);
26749             store.on("add", this.onAdd, this);
26750             store.on("remove", this.onRemove, this);
26751             store.on("update", this.onUpdate, this);
26752             store.on("clear", this.refresh, this);
26753             store.on("beforeload", this.onBeforeLoad, this);
26754             store.on("load", this.onLoad, this);
26755             store.on("loadexception", this.onLoad, this);
26756         }
26757         
26758         if(store){
26759             this.refresh();
26760         }
26761     },
26762     /**
26763      * onbeforeLoad - masks the loading area.
26764      *
26765      */
26766     onBeforeLoad : function(store,opts)
26767     {
26768          //Roo.log('onBeforeLoad');   
26769         if (!opts.add) {
26770             this.el.update("");
26771         }
26772         this.el.mask(this.mask ? this.mask : "Loading" ); 
26773     },
26774     onLoad : function ()
26775     {
26776         this.el.unmask();
26777     },
26778     
26779
26780     /**
26781      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26782      * @param {HTMLElement} node
26783      * @return {HTMLElement} The template node
26784      */
26785     findItemFromChild : function(node){
26786         var el = this.dataName  ?
26787             this.el.child('.roo-tpl-' + this.dataName,true) :
26788             this.el.dom; 
26789         
26790         if(!node || node.parentNode == el){
26791                     return node;
26792             }
26793             var p = node.parentNode;
26794             while(p && p != el){
26795             if(p.parentNode == el){
26796                 return p;
26797             }
26798             p = p.parentNode;
26799         }
26800             return null;
26801     },
26802
26803     /** @ignore */
26804     onClick : function(e){
26805         var item = this.findItemFromChild(e.getTarget());
26806         if(item){
26807             var index = this.indexOf(item);
26808             if(this.onItemClick(item, index, e) !== false){
26809                 this.fireEvent("click", this, index, item, e);
26810             }
26811         }else{
26812             this.clearSelections();
26813         }
26814     },
26815
26816     /** @ignore */
26817     onContextMenu : function(e){
26818         var item = this.findItemFromChild(e.getTarget());
26819         if(item){
26820             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26821         }
26822     },
26823
26824     /** @ignore */
26825     onDblClick : function(e){
26826         var item = this.findItemFromChild(e.getTarget());
26827         if(item){
26828             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26829         }
26830     },
26831
26832     onItemClick : function(item, index, e)
26833     {
26834         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26835             return false;
26836         }
26837         if (this.toggleSelect) {
26838             var m = this.isSelected(item) ? 'unselect' : 'select';
26839             //Roo.log(m);
26840             var _t = this;
26841             _t[m](item, true, false);
26842             return true;
26843         }
26844         if(this.multiSelect || this.singleSelect){
26845             if(this.multiSelect && e.shiftKey && this.lastSelection){
26846                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
26847             }else{
26848                 this.select(item, this.multiSelect && e.ctrlKey);
26849                 this.lastSelection = item;
26850             }
26851             
26852             if(!this.tickable){
26853                 e.preventDefault();
26854             }
26855             
26856         }
26857         return true;
26858     },
26859
26860     /**
26861      * Get the number of selected nodes.
26862      * @return {Number}
26863      */
26864     getSelectionCount : function(){
26865         return this.selections.length;
26866     },
26867
26868     /**
26869      * Get the currently selected nodes.
26870      * @return {Array} An array of HTMLElements
26871      */
26872     getSelectedNodes : function(){
26873         return this.selections;
26874     },
26875
26876     /**
26877      * Get the indexes of the selected nodes.
26878      * @return {Array}
26879      */
26880     getSelectedIndexes : function(){
26881         var indexes = [], s = this.selections;
26882         for(var i = 0, len = s.length; i < len; i++){
26883             indexes.push(s[i].nodeIndex);
26884         }
26885         return indexes;
26886     },
26887
26888     /**
26889      * Clear all selections
26890      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
26891      */
26892     clearSelections : function(suppressEvent){
26893         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
26894             this.cmp.elements = this.selections;
26895             this.cmp.removeClass(this.selectedClass);
26896             this.selections = [];
26897             if(!suppressEvent){
26898                 this.fireEvent("selectionchange", this, this.selections);
26899             }
26900         }
26901     },
26902
26903     /**
26904      * Returns true if the passed node is selected
26905      * @param {HTMLElement/Number} node The node or node index
26906      * @return {Boolean}
26907      */
26908     isSelected : function(node){
26909         var s = this.selections;
26910         if(s.length < 1){
26911             return false;
26912         }
26913         node = this.getNode(node);
26914         return s.indexOf(node) !== -1;
26915     },
26916
26917     /**
26918      * Selects nodes.
26919      * @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
26920      * @param {Boolean} keepExisting (optional) true to keep existing selections
26921      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26922      */
26923     select : function(nodeInfo, keepExisting, suppressEvent){
26924         if(nodeInfo instanceof Array){
26925             if(!keepExisting){
26926                 this.clearSelections(true);
26927             }
26928             for(var i = 0, len = nodeInfo.length; i < len; i++){
26929                 this.select(nodeInfo[i], true, true);
26930             }
26931             return;
26932         } 
26933         var node = this.getNode(nodeInfo);
26934         if(!node || this.isSelected(node)){
26935             return; // already selected.
26936         }
26937         if(!keepExisting){
26938             this.clearSelections(true);
26939         }
26940         
26941         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
26942             Roo.fly(node).addClass(this.selectedClass);
26943             this.selections.push(node);
26944             if(!suppressEvent){
26945                 this.fireEvent("selectionchange", this, this.selections);
26946             }
26947         }
26948         
26949         
26950     },
26951       /**
26952      * Unselects nodes.
26953      * @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
26954      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
26955      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
26956      */
26957     unselect : function(nodeInfo, keepExisting, suppressEvent)
26958     {
26959         if(nodeInfo instanceof Array){
26960             Roo.each(this.selections, function(s) {
26961                 this.unselect(s, nodeInfo);
26962             }, this);
26963             return;
26964         }
26965         var node = this.getNode(nodeInfo);
26966         if(!node || !this.isSelected(node)){
26967             //Roo.log("not selected");
26968             return; // not selected.
26969         }
26970         // fireevent???
26971         var ns = [];
26972         Roo.each(this.selections, function(s) {
26973             if (s == node ) {
26974                 Roo.fly(node).removeClass(this.selectedClass);
26975
26976                 return;
26977             }
26978             ns.push(s);
26979         },this);
26980         
26981         this.selections= ns;
26982         this.fireEvent("selectionchange", this, this.selections);
26983     },
26984
26985     /**
26986      * Gets a template node.
26987      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
26988      * @return {HTMLElement} The node or null if it wasn't found
26989      */
26990     getNode : function(nodeInfo){
26991         if(typeof nodeInfo == "string"){
26992             return document.getElementById(nodeInfo);
26993         }else if(typeof nodeInfo == "number"){
26994             return this.nodes[nodeInfo];
26995         }
26996         return nodeInfo;
26997     },
26998
26999     /**
27000      * Gets a range template nodes.
27001      * @param {Number} startIndex
27002      * @param {Number} endIndex
27003      * @return {Array} An array of nodes
27004      */
27005     getNodes : function(start, end){
27006         var ns = this.nodes;
27007         start = start || 0;
27008         end = typeof end == "undefined" ? ns.length - 1 : end;
27009         var nodes = [];
27010         if(start <= end){
27011             for(var i = start; i <= end; i++){
27012                 nodes.push(ns[i]);
27013             }
27014         } else{
27015             for(var i = start; i >= end; i--){
27016                 nodes.push(ns[i]);
27017             }
27018         }
27019         return nodes;
27020     },
27021
27022     /**
27023      * Finds the index of the passed node
27024      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27025      * @return {Number} The index of the node or -1
27026      */
27027     indexOf : function(node){
27028         node = this.getNode(node);
27029         if(typeof node.nodeIndex == "number"){
27030             return node.nodeIndex;
27031         }
27032         var ns = this.nodes;
27033         for(var i = 0, len = ns.length; i < len; i++){
27034             if(ns[i] == node){
27035                 return i;
27036             }
27037         }
27038         return -1;
27039     }
27040 });
27041 /*
27042  * Based on:
27043  * Ext JS Library 1.1.1
27044  * Copyright(c) 2006-2007, Ext JS, LLC.
27045  *
27046  * Originally Released Under LGPL - original licence link has changed is not relivant.
27047  *
27048  * Fork - LGPL
27049  * <script type="text/javascript">
27050  */
27051
27052 /**
27053  * @class Roo.JsonView
27054  * @extends Roo.View
27055  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27056 <pre><code>
27057 var view = new Roo.JsonView({
27058     container: "my-element",
27059     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27060     multiSelect: true, 
27061     jsonRoot: "data" 
27062 });
27063
27064 // listen for node click?
27065 view.on("click", function(vw, index, node, e){
27066     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27067 });
27068
27069 // direct load of JSON data
27070 view.load("foobar.php");
27071
27072 // Example from my blog list
27073 var tpl = new Roo.Template(
27074     '&lt;div class="entry"&gt;' +
27075     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27076     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27077     "&lt;/div&gt;&lt;hr /&gt;"
27078 );
27079
27080 var moreView = new Roo.JsonView({
27081     container :  "entry-list", 
27082     template : tpl,
27083     jsonRoot: "posts"
27084 });
27085 moreView.on("beforerender", this.sortEntries, this);
27086 moreView.load({
27087     url: "/blog/get-posts.php",
27088     params: "allposts=true",
27089     text: "Loading Blog Entries..."
27090 });
27091 </code></pre>
27092
27093 * Note: old code is supported with arguments : (container, template, config)
27094
27095
27096  * @constructor
27097  * Create a new JsonView
27098  * 
27099  * @param {Object} config The config object
27100  * 
27101  */
27102 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27103     
27104     
27105     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27106
27107     var um = this.el.getUpdateManager();
27108     um.setRenderer(this);
27109     um.on("update", this.onLoad, this);
27110     um.on("failure", this.onLoadException, this);
27111
27112     /**
27113      * @event beforerender
27114      * Fires before rendering of the downloaded JSON data.
27115      * @param {Roo.JsonView} this
27116      * @param {Object} data The JSON data loaded
27117      */
27118     /**
27119      * @event load
27120      * Fires when data is loaded.
27121      * @param {Roo.JsonView} this
27122      * @param {Object} data The JSON data loaded
27123      * @param {Object} response The raw Connect response object
27124      */
27125     /**
27126      * @event loadexception
27127      * Fires when loading fails.
27128      * @param {Roo.JsonView} this
27129      * @param {Object} response The raw Connect response object
27130      */
27131     this.addEvents({
27132         'beforerender' : true,
27133         'load' : true,
27134         'loadexception' : true
27135     });
27136 };
27137 Roo.extend(Roo.JsonView, Roo.View, {
27138     /**
27139      * @type {String} The root property in the loaded JSON object that contains the data
27140      */
27141     jsonRoot : "",
27142
27143     /**
27144      * Refreshes the view.
27145      */
27146     refresh : function(){
27147         this.clearSelections();
27148         this.el.update("");
27149         var html = [];
27150         var o = this.jsonData;
27151         if(o && o.length > 0){
27152             for(var i = 0, len = o.length; i < len; i++){
27153                 var data = this.prepareData(o[i], i, o);
27154                 html[html.length] = this.tpl.apply(data);
27155             }
27156         }else{
27157             html.push(this.emptyText);
27158         }
27159         this.el.update(html.join(""));
27160         this.nodes = this.el.dom.childNodes;
27161         this.updateIndexes(0);
27162     },
27163
27164     /**
27165      * 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.
27166      * @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:
27167      <pre><code>
27168      view.load({
27169          url: "your-url.php",
27170          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27171          callback: yourFunction,
27172          scope: yourObject, //(optional scope)
27173          discardUrl: false,
27174          nocache: false,
27175          text: "Loading...",
27176          timeout: 30,
27177          scripts: false
27178      });
27179      </code></pre>
27180      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27181      * 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.
27182      * @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}
27183      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27184      * @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.
27185      */
27186     load : function(){
27187         var um = this.el.getUpdateManager();
27188         um.update.apply(um, arguments);
27189     },
27190
27191     // note - render is a standard framework call...
27192     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27193     render : function(el, response){
27194         
27195         this.clearSelections();
27196         this.el.update("");
27197         var o;
27198         try{
27199             if (response != '') {
27200                 o = Roo.util.JSON.decode(response.responseText);
27201                 if(this.jsonRoot){
27202                     
27203                     o = o[this.jsonRoot];
27204                 }
27205             }
27206         } catch(e){
27207         }
27208         /**
27209          * The current JSON data or null
27210          */
27211         this.jsonData = o;
27212         this.beforeRender();
27213         this.refresh();
27214     },
27215
27216 /**
27217  * Get the number of records in the current JSON dataset
27218  * @return {Number}
27219  */
27220     getCount : function(){
27221         return this.jsonData ? this.jsonData.length : 0;
27222     },
27223
27224 /**
27225  * Returns the JSON object for the specified node(s)
27226  * @param {HTMLElement/Array} node The node or an array of nodes
27227  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27228  * you get the JSON object for the node
27229  */
27230     getNodeData : function(node){
27231         if(node instanceof Array){
27232             var data = [];
27233             for(var i = 0, len = node.length; i < len; i++){
27234                 data.push(this.getNodeData(node[i]));
27235             }
27236             return data;
27237         }
27238         return this.jsonData[this.indexOf(node)] || null;
27239     },
27240
27241     beforeRender : function(){
27242         this.snapshot = this.jsonData;
27243         if(this.sortInfo){
27244             this.sort.apply(this, this.sortInfo);
27245         }
27246         this.fireEvent("beforerender", this, this.jsonData);
27247     },
27248
27249     onLoad : function(el, o){
27250         this.fireEvent("load", this, this.jsonData, o);
27251     },
27252
27253     onLoadException : function(el, o){
27254         this.fireEvent("loadexception", this, o);
27255     },
27256
27257 /**
27258  * Filter the data by a specific property.
27259  * @param {String} property A property on your JSON objects
27260  * @param {String/RegExp} value Either string that the property values
27261  * should start with, or a RegExp to test against the property
27262  */
27263     filter : function(property, value){
27264         if(this.jsonData){
27265             var data = [];
27266             var ss = this.snapshot;
27267             if(typeof value == "string"){
27268                 var vlen = value.length;
27269                 if(vlen == 0){
27270                     this.clearFilter();
27271                     return;
27272                 }
27273                 value = value.toLowerCase();
27274                 for(var i = 0, len = ss.length; i < len; i++){
27275                     var o = ss[i];
27276                     if(o[property].substr(0, vlen).toLowerCase() == value){
27277                         data.push(o);
27278                     }
27279                 }
27280             } else if(value.exec){ // regex?
27281                 for(var i = 0, len = ss.length; i < len; i++){
27282                     var o = ss[i];
27283                     if(value.test(o[property])){
27284                         data.push(o);
27285                     }
27286                 }
27287             } else{
27288                 return;
27289             }
27290             this.jsonData = data;
27291             this.refresh();
27292         }
27293     },
27294
27295 /**
27296  * Filter by a function. The passed function will be called with each
27297  * object in the current dataset. If the function returns true the value is kept,
27298  * otherwise it is filtered.
27299  * @param {Function} fn
27300  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27301  */
27302     filterBy : function(fn, scope){
27303         if(this.jsonData){
27304             var data = [];
27305             var ss = this.snapshot;
27306             for(var i = 0, len = ss.length; i < len; i++){
27307                 var o = ss[i];
27308                 if(fn.call(scope || this, o)){
27309                     data.push(o);
27310                 }
27311             }
27312             this.jsonData = data;
27313             this.refresh();
27314         }
27315     },
27316
27317 /**
27318  * Clears the current filter.
27319  */
27320     clearFilter : function(){
27321         if(this.snapshot && this.jsonData != this.snapshot){
27322             this.jsonData = this.snapshot;
27323             this.refresh();
27324         }
27325     },
27326
27327
27328 /**
27329  * Sorts the data for this view and refreshes it.
27330  * @param {String} property A property on your JSON objects to sort on
27331  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27332  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27333  */
27334     sort : function(property, dir, sortType){
27335         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27336         if(this.jsonData){
27337             var p = property;
27338             var dsc = dir && dir.toLowerCase() == "desc";
27339             var f = function(o1, o2){
27340                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27341                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27342                 ;
27343                 if(v1 < v2){
27344                     return dsc ? +1 : -1;
27345                 } else if(v1 > v2){
27346                     return dsc ? -1 : +1;
27347                 } else{
27348                     return 0;
27349                 }
27350             };
27351             this.jsonData.sort(f);
27352             this.refresh();
27353             if(this.jsonData != this.snapshot){
27354                 this.snapshot.sort(f);
27355             }
27356         }
27357     }
27358 });/*
27359  * Based on:
27360  * Ext JS Library 1.1.1
27361  * Copyright(c) 2006-2007, Ext JS, LLC.
27362  *
27363  * Originally Released Under LGPL - original licence link has changed is not relivant.
27364  *
27365  * Fork - LGPL
27366  * <script type="text/javascript">
27367  */
27368  
27369
27370 /**
27371  * @class Roo.ColorPalette
27372  * @extends Roo.Component
27373  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27374  * Here's an example of typical usage:
27375  * <pre><code>
27376 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27377 cp.render('my-div');
27378
27379 cp.on('select', function(palette, selColor){
27380     // do something with selColor
27381 });
27382 </code></pre>
27383  * @constructor
27384  * Create a new ColorPalette
27385  * @param {Object} config The config object
27386  */
27387 Roo.ColorPalette = function(config){
27388     Roo.ColorPalette.superclass.constructor.call(this, config);
27389     this.addEvents({
27390         /**
27391              * @event select
27392              * Fires when a color is selected
27393              * @param {ColorPalette} this
27394              * @param {String} color The 6-digit color hex code (without the # symbol)
27395              */
27396         select: true
27397     });
27398
27399     if(this.handler){
27400         this.on("select", this.handler, this.scope, true);
27401     }
27402 };
27403 Roo.extend(Roo.ColorPalette, Roo.Component, {
27404     /**
27405      * @cfg {String} itemCls
27406      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27407      */
27408     itemCls : "x-color-palette",
27409     /**
27410      * @cfg {String} value
27411      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27412      * the hex codes are case-sensitive.
27413      */
27414     value : null,
27415     clickEvent:'click',
27416     // private
27417     ctype: "Roo.ColorPalette",
27418
27419     /**
27420      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27421      */
27422     allowReselect : false,
27423
27424     /**
27425      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27426      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27427      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27428      * of colors with the width setting until the box is symmetrical.</p>
27429      * <p>You can override individual colors if needed:</p>
27430      * <pre><code>
27431 var cp = new Roo.ColorPalette();
27432 cp.colors[0] = "FF0000";  // change the first box to red
27433 </code></pre>
27434
27435 Or you can provide a custom array of your own for complete control:
27436 <pre><code>
27437 var cp = new Roo.ColorPalette();
27438 cp.colors = ["000000", "993300", "333300"];
27439 </code></pre>
27440      * @type Array
27441      */
27442     colors : [
27443         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27444         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27445         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27446         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27447         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27448     ],
27449
27450     // private
27451     onRender : function(container, position){
27452         var t = new Roo.MasterTemplate(
27453             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27454         );
27455         var c = this.colors;
27456         for(var i = 0, len = c.length; i < len; i++){
27457             t.add([c[i]]);
27458         }
27459         var el = document.createElement("div");
27460         el.className = this.itemCls;
27461         t.overwrite(el);
27462         container.dom.insertBefore(el, position);
27463         this.el = Roo.get(el);
27464         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27465         if(this.clickEvent != 'click'){
27466             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27467         }
27468     },
27469
27470     // private
27471     afterRender : function(){
27472         Roo.ColorPalette.superclass.afterRender.call(this);
27473         if(this.value){
27474             var s = this.value;
27475             this.value = null;
27476             this.select(s);
27477         }
27478     },
27479
27480     // private
27481     handleClick : function(e, t){
27482         e.preventDefault();
27483         if(!this.disabled){
27484             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27485             this.select(c.toUpperCase());
27486         }
27487     },
27488
27489     /**
27490      * Selects the specified color in the palette (fires the select event)
27491      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27492      */
27493     select : function(color){
27494         color = color.replace("#", "");
27495         if(color != this.value || this.allowReselect){
27496             var el = this.el;
27497             if(this.value){
27498                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27499             }
27500             el.child("a.color-"+color).addClass("x-color-palette-sel");
27501             this.value = color;
27502             this.fireEvent("select", this, color);
27503         }
27504     }
27505 });/*
27506  * Based on:
27507  * Ext JS Library 1.1.1
27508  * Copyright(c) 2006-2007, Ext JS, LLC.
27509  *
27510  * Originally Released Under LGPL - original licence link has changed is not relivant.
27511  *
27512  * Fork - LGPL
27513  * <script type="text/javascript">
27514  */
27515  
27516 /**
27517  * @class Roo.DatePicker
27518  * @extends Roo.Component
27519  * Simple date picker class.
27520  * @constructor
27521  * Create a new DatePicker
27522  * @param {Object} config The config object
27523  */
27524 Roo.DatePicker = function(config){
27525     Roo.DatePicker.superclass.constructor.call(this, config);
27526
27527     this.value = config && config.value ?
27528                  config.value.clearTime() : new Date().clearTime();
27529
27530     this.addEvents({
27531         /**
27532              * @event select
27533              * Fires when a date is selected
27534              * @param {DatePicker} this
27535              * @param {Date} date The selected date
27536              */
27537         'select': true,
27538         /**
27539              * @event monthchange
27540              * Fires when the displayed month changes 
27541              * @param {DatePicker} this
27542              * @param {Date} date The selected month
27543              */
27544         'monthchange': true
27545     });
27546
27547     if(this.handler){
27548         this.on("select", this.handler,  this.scope || this);
27549     }
27550     // build the disabledDatesRE
27551     if(!this.disabledDatesRE && this.disabledDates){
27552         var dd = this.disabledDates;
27553         var re = "(?:";
27554         for(var i = 0; i < dd.length; i++){
27555             re += dd[i];
27556             if(i != dd.length-1) {
27557                 re += "|";
27558             }
27559         }
27560         this.disabledDatesRE = new RegExp(re + ")");
27561     }
27562 };
27563
27564 Roo.extend(Roo.DatePicker, Roo.Component, {
27565     /**
27566      * @cfg {String} todayText
27567      * The text to display on the button that selects the current date (defaults to "Today")
27568      */
27569     todayText : "Today",
27570     /**
27571      * @cfg {String} okText
27572      * The text to display on the ok button
27573      */
27574     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27575     /**
27576      * @cfg {String} cancelText
27577      * The text to display on the cancel button
27578      */
27579     cancelText : "Cancel",
27580     /**
27581      * @cfg {String} todayTip
27582      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27583      */
27584     todayTip : "{0} (Spacebar)",
27585     /**
27586      * @cfg {Date} minDate
27587      * Minimum allowable date (JavaScript date object, defaults to null)
27588      */
27589     minDate : null,
27590     /**
27591      * @cfg {Date} maxDate
27592      * Maximum allowable date (JavaScript date object, defaults to null)
27593      */
27594     maxDate : null,
27595     /**
27596      * @cfg {String} minText
27597      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27598      */
27599     minText : "This date is before the minimum date",
27600     /**
27601      * @cfg {String} maxText
27602      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27603      */
27604     maxText : "This date is after the maximum date",
27605     /**
27606      * @cfg {String} format
27607      * The default date format string which can be overriden for localization support.  The format must be
27608      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27609      */
27610     format : "m/d/y",
27611     /**
27612      * @cfg {Array} disabledDays
27613      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27614      */
27615     disabledDays : null,
27616     /**
27617      * @cfg {String} disabledDaysText
27618      * The tooltip to display when the date falls on a disabled day (defaults to "")
27619      */
27620     disabledDaysText : "",
27621     /**
27622      * @cfg {RegExp} disabledDatesRE
27623      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27624      */
27625     disabledDatesRE : null,
27626     /**
27627      * @cfg {String} disabledDatesText
27628      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27629      */
27630     disabledDatesText : "",
27631     /**
27632      * @cfg {Boolean} constrainToViewport
27633      * True to constrain the date picker to the viewport (defaults to true)
27634      */
27635     constrainToViewport : true,
27636     /**
27637      * @cfg {Array} monthNames
27638      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27639      */
27640     monthNames : Date.monthNames,
27641     /**
27642      * @cfg {Array} dayNames
27643      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27644      */
27645     dayNames : Date.dayNames,
27646     /**
27647      * @cfg {String} nextText
27648      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27649      */
27650     nextText: 'Next Month (Control+Right)',
27651     /**
27652      * @cfg {String} prevText
27653      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27654      */
27655     prevText: 'Previous Month (Control+Left)',
27656     /**
27657      * @cfg {String} monthYearText
27658      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27659      */
27660     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27661     /**
27662      * @cfg {Number} startDay
27663      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27664      */
27665     startDay : 0,
27666     /**
27667      * @cfg {Bool} showClear
27668      * Show a clear button (usefull for date form elements that can be blank.)
27669      */
27670     
27671     showClear: false,
27672     
27673     /**
27674      * Sets the value of the date field
27675      * @param {Date} value The date to set
27676      */
27677     setValue : function(value){
27678         var old = this.value;
27679         
27680         if (typeof(value) == 'string') {
27681          
27682             value = Date.parseDate(value, this.format);
27683         }
27684         if (!value) {
27685             value = new Date();
27686         }
27687         
27688         this.value = value.clearTime(true);
27689         if(this.el){
27690             this.update(this.value);
27691         }
27692     },
27693
27694     /**
27695      * Gets the current selected value of the date field
27696      * @return {Date} The selected date
27697      */
27698     getValue : function(){
27699         return this.value;
27700     },
27701
27702     // private
27703     focus : function(){
27704         if(this.el){
27705             this.update(this.activeDate);
27706         }
27707     },
27708
27709     // privateval
27710     onRender : function(container, position){
27711         
27712         var m = [
27713              '<table cellspacing="0">',
27714                 '<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>',
27715                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27716         var dn = this.dayNames;
27717         for(var i = 0; i < 7; i++){
27718             var d = this.startDay+i;
27719             if(d > 6){
27720                 d = d-7;
27721             }
27722             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27723         }
27724         m[m.length] = "</tr></thead><tbody><tr>";
27725         for(var i = 0; i < 42; i++) {
27726             if(i % 7 == 0 && i != 0){
27727                 m[m.length] = "</tr><tr>";
27728             }
27729             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27730         }
27731         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27732             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27733
27734         var el = document.createElement("div");
27735         el.className = "x-date-picker";
27736         el.innerHTML = m.join("");
27737
27738         container.dom.insertBefore(el, position);
27739
27740         this.el = Roo.get(el);
27741         this.eventEl = Roo.get(el.firstChild);
27742
27743         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27744             handler: this.showPrevMonth,
27745             scope: this,
27746             preventDefault:true,
27747             stopDefault:true
27748         });
27749
27750         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27751             handler: this.showNextMonth,
27752             scope: this,
27753             preventDefault:true,
27754             stopDefault:true
27755         });
27756
27757         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27758
27759         this.monthPicker = this.el.down('div.x-date-mp');
27760         this.monthPicker.enableDisplayMode('block');
27761         
27762         var kn = new Roo.KeyNav(this.eventEl, {
27763             "left" : function(e){
27764                 e.ctrlKey ?
27765                     this.showPrevMonth() :
27766                     this.update(this.activeDate.add("d", -1));
27767             },
27768
27769             "right" : function(e){
27770                 e.ctrlKey ?
27771                     this.showNextMonth() :
27772                     this.update(this.activeDate.add("d", 1));
27773             },
27774
27775             "up" : function(e){
27776                 e.ctrlKey ?
27777                     this.showNextYear() :
27778                     this.update(this.activeDate.add("d", -7));
27779             },
27780
27781             "down" : function(e){
27782                 e.ctrlKey ?
27783                     this.showPrevYear() :
27784                     this.update(this.activeDate.add("d", 7));
27785             },
27786
27787             "pageUp" : function(e){
27788                 this.showNextMonth();
27789             },
27790
27791             "pageDown" : function(e){
27792                 this.showPrevMonth();
27793             },
27794
27795             "enter" : function(e){
27796                 e.stopPropagation();
27797                 return true;
27798             },
27799
27800             scope : this
27801         });
27802
27803         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27804
27805         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27806
27807         this.el.unselectable();
27808         
27809         this.cells = this.el.select("table.x-date-inner tbody td");
27810         this.textNodes = this.el.query("table.x-date-inner tbody span");
27811
27812         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27813             text: "&#160;",
27814             tooltip: this.monthYearText
27815         });
27816
27817         this.mbtn.on('click', this.showMonthPicker, this);
27818         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27819
27820
27821         var today = (new Date()).dateFormat(this.format);
27822         
27823         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27824         if (this.showClear) {
27825             baseTb.add( new Roo.Toolbar.Fill());
27826         }
27827         baseTb.add({
27828             text: String.format(this.todayText, today),
27829             tooltip: String.format(this.todayTip, today),
27830             handler: this.selectToday,
27831             scope: this
27832         });
27833         
27834         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27835             
27836         //});
27837         if (this.showClear) {
27838             
27839             baseTb.add( new Roo.Toolbar.Fill());
27840             baseTb.add({
27841                 text: '&#160;',
27842                 cls: 'x-btn-icon x-btn-clear',
27843                 handler: function() {
27844                     //this.value = '';
27845                     this.fireEvent("select", this, '');
27846                 },
27847                 scope: this
27848             });
27849         }
27850         
27851         
27852         if(Roo.isIE){
27853             this.el.repaint();
27854         }
27855         this.update(this.value);
27856     },
27857
27858     createMonthPicker : function(){
27859         if(!this.monthPicker.dom.firstChild){
27860             var buf = ['<table border="0" cellspacing="0">'];
27861             for(var i = 0; i < 6; i++){
27862                 buf.push(
27863                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
27864                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
27865                     i == 0 ?
27866                     '<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>' :
27867                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
27868                 );
27869             }
27870             buf.push(
27871                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
27872                     this.okText,
27873                     '</button><button type="button" class="x-date-mp-cancel">',
27874                     this.cancelText,
27875                     '</button></td></tr>',
27876                 '</table>'
27877             );
27878             this.monthPicker.update(buf.join(''));
27879             this.monthPicker.on('click', this.onMonthClick, this);
27880             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
27881
27882             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
27883             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
27884
27885             this.mpMonths.each(function(m, a, i){
27886                 i += 1;
27887                 if((i%2) == 0){
27888                     m.dom.xmonth = 5 + Math.round(i * .5);
27889                 }else{
27890                     m.dom.xmonth = Math.round((i-1) * .5);
27891                 }
27892             });
27893         }
27894     },
27895
27896     showMonthPicker : function(){
27897         this.createMonthPicker();
27898         var size = this.el.getSize();
27899         this.monthPicker.setSize(size);
27900         this.monthPicker.child('table').setSize(size);
27901
27902         this.mpSelMonth = (this.activeDate || this.value).getMonth();
27903         this.updateMPMonth(this.mpSelMonth);
27904         this.mpSelYear = (this.activeDate || this.value).getFullYear();
27905         this.updateMPYear(this.mpSelYear);
27906
27907         this.monthPicker.slideIn('t', {duration:.2});
27908     },
27909
27910     updateMPYear : function(y){
27911         this.mpyear = y;
27912         var ys = this.mpYears.elements;
27913         for(var i = 1; i <= 10; i++){
27914             var td = ys[i-1], y2;
27915             if((i%2) == 0){
27916                 y2 = y + Math.round(i * .5);
27917                 td.firstChild.innerHTML = y2;
27918                 td.xyear = y2;
27919             }else{
27920                 y2 = y - (5-Math.round(i * .5));
27921                 td.firstChild.innerHTML = y2;
27922                 td.xyear = y2;
27923             }
27924             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
27925         }
27926     },
27927
27928     updateMPMonth : function(sm){
27929         this.mpMonths.each(function(m, a, i){
27930             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
27931         });
27932     },
27933
27934     selectMPMonth: function(m){
27935         
27936     },
27937
27938     onMonthClick : function(e, t){
27939         e.stopEvent();
27940         var el = new Roo.Element(t), pn;
27941         if(el.is('button.x-date-mp-cancel')){
27942             this.hideMonthPicker();
27943         }
27944         else if(el.is('button.x-date-mp-ok')){
27945             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27946             this.hideMonthPicker();
27947         }
27948         else if(pn = el.up('td.x-date-mp-month', 2)){
27949             this.mpMonths.removeClass('x-date-mp-sel');
27950             pn.addClass('x-date-mp-sel');
27951             this.mpSelMonth = pn.dom.xmonth;
27952         }
27953         else if(pn = el.up('td.x-date-mp-year', 2)){
27954             this.mpYears.removeClass('x-date-mp-sel');
27955             pn.addClass('x-date-mp-sel');
27956             this.mpSelYear = pn.dom.xyear;
27957         }
27958         else if(el.is('a.x-date-mp-prev')){
27959             this.updateMPYear(this.mpyear-10);
27960         }
27961         else if(el.is('a.x-date-mp-next')){
27962             this.updateMPYear(this.mpyear+10);
27963         }
27964     },
27965
27966     onMonthDblClick : function(e, t){
27967         e.stopEvent();
27968         var el = new Roo.Element(t), pn;
27969         if(pn = el.up('td.x-date-mp-month', 2)){
27970             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
27971             this.hideMonthPicker();
27972         }
27973         else if(pn = el.up('td.x-date-mp-year', 2)){
27974             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
27975             this.hideMonthPicker();
27976         }
27977     },
27978
27979     hideMonthPicker : function(disableAnim){
27980         if(this.monthPicker){
27981             if(disableAnim === true){
27982                 this.monthPicker.hide();
27983             }else{
27984                 this.monthPicker.slideOut('t', {duration:.2});
27985             }
27986         }
27987     },
27988
27989     // private
27990     showPrevMonth : function(e){
27991         this.update(this.activeDate.add("mo", -1));
27992     },
27993
27994     // private
27995     showNextMonth : function(e){
27996         this.update(this.activeDate.add("mo", 1));
27997     },
27998
27999     // private
28000     showPrevYear : function(){
28001         this.update(this.activeDate.add("y", -1));
28002     },
28003
28004     // private
28005     showNextYear : function(){
28006         this.update(this.activeDate.add("y", 1));
28007     },
28008
28009     // private
28010     handleMouseWheel : function(e){
28011         var delta = e.getWheelDelta();
28012         if(delta > 0){
28013             this.showPrevMonth();
28014             e.stopEvent();
28015         } else if(delta < 0){
28016             this.showNextMonth();
28017             e.stopEvent();
28018         }
28019     },
28020
28021     // private
28022     handleDateClick : function(e, t){
28023         e.stopEvent();
28024         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28025             this.setValue(new Date(t.dateValue));
28026             this.fireEvent("select", this, this.value);
28027         }
28028     },
28029
28030     // private
28031     selectToday : function(){
28032         this.setValue(new Date().clearTime());
28033         this.fireEvent("select", this, this.value);
28034     },
28035
28036     // private
28037     update : function(date)
28038     {
28039         var vd = this.activeDate;
28040         this.activeDate = date;
28041         if(vd && this.el){
28042             var t = date.getTime();
28043             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28044                 this.cells.removeClass("x-date-selected");
28045                 this.cells.each(function(c){
28046                    if(c.dom.firstChild.dateValue == t){
28047                        c.addClass("x-date-selected");
28048                        setTimeout(function(){
28049                             try{c.dom.firstChild.focus();}catch(e){}
28050                        }, 50);
28051                        return false;
28052                    }
28053                 });
28054                 return;
28055             }
28056         }
28057         
28058         var days = date.getDaysInMonth();
28059         var firstOfMonth = date.getFirstDateOfMonth();
28060         var startingPos = firstOfMonth.getDay()-this.startDay;
28061
28062         if(startingPos <= this.startDay){
28063             startingPos += 7;
28064         }
28065
28066         var pm = date.add("mo", -1);
28067         var prevStart = pm.getDaysInMonth()-startingPos;
28068
28069         var cells = this.cells.elements;
28070         var textEls = this.textNodes;
28071         days += startingPos;
28072
28073         // convert everything to numbers so it's fast
28074         var day = 86400000;
28075         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28076         var today = new Date().clearTime().getTime();
28077         var sel = date.clearTime().getTime();
28078         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28079         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28080         var ddMatch = this.disabledDatesRE;
28081         var ddText = this.disabledDatesText;
28082         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28083         var ddaysText = this.disabledDaysText;
28084         var format = this.format;
28085
28086         var setCellClass = function(cal, cell){
28087             cell.title = "";
28088             var t = d.getTime();
28089             cell.firstChild.dateValue = t;
28090             if(t == today){
28091                 cell.className += " x-date-today";
28092                 cell.title = cal.todayText;
28093             }
28094             if(t == sel){
28095                 cell.className += " x-date-selected";
28096                 setTimeout(function(){
28097                     try{cell.firstChild.focus();}catch(e){}
28098                 }, 50);
28099             }
28100             // disabling
28101             if(t < min) {
28102                 cell.className = " x-date-disabled";
28103                 cell.title = cal.minText;
28104                 return;
28105             }
28106             if(t > max) {
28107                 cell.className = " x-date-disabled";
28108                 cell.title = cal.maxText;
28109                 return;
28110             }
28111             if(ddays){
28112                 if(ddays.indexOf(d.getDay()) != -1){
28113                     cell.title = ddaysText;
28114                     cell.className = " x-date-disabled";
28115                 }
28116             }
28117             if(ddMatch && format){
28118                 var fvalue = d.dateFormat(format);
28119                 if(ddMatch.test(fvalue)){
28120                     cell.title = ddText.replace("%0", fvalue);
28121                     cell.className = " x-date-disabled";
28122                 }
28123             }
28124         };
28125
28126         var i = 0;
28127         for(; i < startingPos; i++) {
28128             textEls[i].innerHTML = (++prevStart);
28129             d.setDate(d.getDate()+1);
28130             cells[i].className = "x-date-prevday";
28131             setCellClass(this, cells[i]);
28132         }
28133         for(; i < days; i++){
28134             intDay = i - startingPos + 1;
28135             textEls[i].innerHTML = (intDay);
28136             d.setDate(d.getDate()+1);
28137             cells[i].className = "x-date-active";
28138             setCellClass(this, cells[i]);
28139         }
28140         var extraDays = 0;
28141         for(; i < 42; i++) {
28142              textEls[i].innerHTML = (++extraDays);
28143              d.setDate(d.getDate()+1);
28144              cells[i].className = "x-date-nextday";
28145              setCellClass(this, cells[i]);
28146         }
28147
28148         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28149         this.fireEvent('monthchange', this, date);
28150         
28151         if(!this.internalRender){
28152             var main = this.el.dom.firstChild;
28153             var w = main.offsetWidth;
28154             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28155             Roo.fly(main).setWidth(w);
28156             this.internalRender = true;
28157             // opera does not respect the auto grow header center column
28158             // then, after it gets a width opera refuses to recalculate
28159             // without a second pass
28160             if(Roo.isOpera && !this.secondPass){
28161                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28162                 this.secondPass = true;
28163                 this.update.defer(10, this, [date]);
28164             }
28165         }
28166         
28167         
28168     }
28169 });        /*
28170  * Based on:
28171  * Ext JS Library 1.1.1
28172  * Copyright(c) 2006-2007, Ext JS, LLC.
28173  *
28174  * Originally Released Under LGPL - original licence link has changed is not relivant.
28175  *
28176  * Fork - LGPL
28177  * <script type="text/javascript">
28178  */
28179 /**
28180  * @class Roo.TabPanel
28181  * @extends Roo.util.Observable
28182  * A lightweight tab container.
28183  * <br><br>
28184  * Usage:
28185  * <pre><code>
28186 // basic tabs 1, built from existing content
28187 var tabs = new Roo.TabPanel("tabs1");
28188 tabs.addTab("script", "View Script");
28189 tabs.addTab("markup", "View Markup");
28190 tabs.activate("script");
28191
28192 // more advanced tabs, built from javascript
28193 var jtabs = new Roo.TabPanel("jtabs");
28194 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28195
28196 // set up the UpdateManager
28197 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28198 var updater = tab2.getUpdateManager();
28199 updater.setDefaultUrl("ajax1.htm");
28200 tab2.on('activate', updater.refresh, updater, true);
28201
28202 // Use setUrl for Ajax loading
28203 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28204 tab3.setUrl("ajax2.htm", null, true);
28205
28206 // Disabled tab
28207 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28208 tab4.disable();
28209
28210 jtabs.activate("jtabs-1");
28211  * </code></pre>
28212  * @constructor
28213  * Create a new TabPanel.
28214  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28215  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28216  */
28217 Roo.TabPanel = function(container, config){
28218     /**
28219     * The container element for this TabPanel.
28220     * @type Roo.Element
28221     */
28222     this.el = Roo.get(container, true);
28223     if(config){
28224         if(typeof config == "boolean"){
28225             this.tabPosition = config ? "bottom" : "top";
28226         }else{
28227             Roo.apply(this, config);
28228         }
28229     }
28230     if(this.tabPosition == "bottom"){
28231         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28232         this.el.addClass("x-tabs-bottom");
28233     }
28234     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28235     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28236     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28237     if(Roo.isIE){
28238         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28239     }
28240     if(this.tabPosition != "bottom"){
28241         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28242          * @type Roo.Element
28243          */
28244         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28245         this.el.addClass("x-tabs-top");
28246     }
28247     this.items = [];
28248
28249     this.bodyEl.setStyle("position", "relative");
28250
28251     this.active = null;
28252     this.activateDelegate = this.activate.createDelegate(this);
28253
28254     this.addEvents({
28255         /**
28256          * @event tabchange
28257          * Fires when the active tab changes
28258          * @param {Roo.TabPanel} this
28259          * @param {Roo.TabPanelItem} activePanel The new active tab
28260          */
28261         "tabchange": true,
28262         /**
28263          * @event beforetabchange
28264          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28265          * @param {Roo.TabPanel} this
28266          * @param {Object} e Set cancel to true on this object to cancel the tab change
28267          * @param {Roo.TabPanelItem} tab The tab being changed to
28268          */
28269         "beforetabchange" : true
28270     });
28271
28272     Roo.EventManager.onWindowResize(this.onResize, this);
28273     this.cpad = this.el.getPadding("lr");
28274     this.hiddenCount = 0;
28275
28276
28277     // toolbar on the tabbar support...
28278     if (this.toolbar) {
28279         var tcfg = this.toolbar;
28280         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28281         this.toolbar = new Roo.Toolbar(tcfg);
28282         if (Roo.isSafari) {
28283             var tbl = tcfg.container.child('table', true);
28284             tbl.setAttribute('width', '100%');
28285         }
28286         
28287     }
28288    
28289
28290
28291     Roo.TabPanel.superclass.constructor.call(this);
28292 };
28293
28294 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28295     /*
28296      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28297      */
28298     tabPosition : "top",
28299     /*
28300      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28301      */
28302     currentTabWidth : 0,
28303     /*
28304      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28305      */
28306     minTabWidth : 40,
28307     /*
28308      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28309      */
28310     maxTabWidth : 250,
28311     /*
28312      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28313      */
28314     preferredTabWidth : 175,
28315     /*
28316      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28317      */
28318     resizeTabs : false,
28319     /*
28320      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28321      */
28322     monitorResize : true,
28323     /*
28324      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28325      */
28326     toolbar : false,
28327
28328     /**
28329      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28330      * @param {String} id The id of the div to use <b>or create</b>
28331      * @param {String} text The text for the tab
28332      * @param {String} content (optional) Content to put in the TabPanelItem body
28333      * @param {Boolean} closable (optional) True to create a close icon on the tab
28334      * @return {Roo.TabPanelItem} The created TabPanelItem
28335      */
28336     addTab : function(id, text, content, closable){
28337         var item = new Roo.TabPanelItem(this, id, text, closable);
28338         this.addTabItem(item);
28339         if(content){
28340             item.setContent(content);
28341         }
28342         return item;
28343     },
28344
28345     /**
28346      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28347      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28348      * @return {Roo.TabPanelItem}
28349      */
28350     getTab : function(id){
28351         return this.items[id];
28352     },
28353
28354     /**
28355      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28356      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28357      */
28358     hideTab : function(id){
28359         var t = this.items[id];
28360         if(!t.isHidden()){
28361            t.setHidden(true);
28362            this.hiddenCount++;
28363            this.autoSizeTabs();
28364         }
28365     },
28366
28367     /**
28368      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28369      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28370      */
28371     unhideTab : function(id){
28372         var t = this.items[id];
28373         if(t.isHidden()){
28374            t.setHidden(false);
28375            this.hiddenCount--;
28376            this.autoSizeTabs();
28377         }
28378     },
28379
28380     /**
28381      * Adds an existing {@link Roo.TabPanelItem}.
28382      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28383      */
28384     addTabItem : function(item){
28385         this.items[item.id] = item;
28386         this.items.push(item);
28387         if(this.resizeTabs){
28388            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28389            this.autoSizeTabs();
28390         }else{
28391             item.autoSize();
28392         }
28393     },
28394
28395     /**
28396      * Removes a {@link Roo.TabPanelItem}.
28397      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28398      */
28399     removeTab : function(id){
28400         var items = this.items;
28401         var tab = items[id];
28402         if(!tab) { return; }
28403         var index = items.indexOf(tab);
28404         if(this.active == tab && items.length > 1){
28405             var newTab = this.getNextAvailable(index);
28406             if(newTab) {
28407                 newTab.activate();
28408             }
28409         }
28410         this.stripEl.dom.removeChild(tab.pnode.dom);
28411         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28412             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28413         }
28414         items.splice(index, 1);
28415         delete this.items[tab.id];
28416         tab.fireEvent("close", tab);
28417         tab.purgeListeners();
28418         this.autoSizeTabs();
28419     },
28420
28421     getNextAvailable : function(start){
28422         var items = this.items;
28423         var index = start;
28424         // look for a next tab that will slide over to
28425         // replace the one being removed
28426         while(index < items.length){
28427             var item = items[++index];
28428             if(item && !item.isHidden()){
28429                 return item;
28430             }
28431         }
28432         // if one isn't found select the previous tab (on the left)
28433         index = start;
28434         while(index >= 0){
28435             var item = items[--index];
28436             if(item && !item.isHidden()){
28437                 return item;
28438             }
28439         }
28440         return null;
28441     },
28442
28443     /**
28444      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28445      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28446      */
28447     disableTab : function(id){
28448         var tab = this.items[id];
28449         if(tab && this.active != tab){
28450             tab.disable();
28451         }
28452     },
28453
28454     /**
28455      * Enables a {@link Roo.TabPanelItem} that is disabled.
28456      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28457      */
28458     enableTab : function(id){
28459         var tab = this.items[id];
28460         tab.enable();
28461     },
28462
28463     /**
28464      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28465      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28466      * @return {Roo.TabPanelItem} The TabPanelItem.
28467      */
28468     activate : function(id){
28469         var tab = this.items[id];
28470         if(!tab){
28471             return null;
28472         }
28473         if(tab == this.active || tab.disabled){
28474             return tab;
28475         }
28476         var e = {};
28477         this.fireEvent("beforetabchange", this, e, tab);
28478         if(e.cancel !== true && !tab.disabled){
28479             if(this.active){
28480                 this.active.hide();
28481             }
28482             this.active = this.items[id];
28483             this.active.show();
28484             this.fireEvent("tabchange", this, this.active);
28485         }
28486         return tab;
28487     },
28488
28489     /**
28490      * Gets the active {@link Roo.TabPanelItem}.
28491      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28492      */
28493     getActiveTab : function(){
28494         return this.active;
28495     },
28496
28497     /**
28498      * Updates the tab body element to fit the height of the container element
28499      * for overflow scrolling
28500      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28501      */
28502     syncHeight : function(targetHeight){
28503         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28504         var bm = this.bodyEl.getMargins();
28505         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28506         this.bodyEl.setHeight(newHeight);
28507         return newHeight;
28508     },
28509
28510     onResize : function(){
28511         if(this.monitorResize){
28512             this.autoSizeTabs();
28513         }
28514     },
28515
28516     /**
28517      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28518      */
28519     beginUpdate : function(){
28520         this.updating = true;
28521     },
28522
28523     /**
28524      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28525      */
28526     endUpdate : function(){
28527         this.updating = false;
28528         this.autoSizeTabs();
28529     },
28530
28531     /**
28532      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28533      */
28534     autoSizeTabs : function(){
28535         var count = this.items.length;
28536         var vcount = count - this.hiddenCount;
28537         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28538             return;
28539         }
28540         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28541         var availWidth = Math.floor(w / vcount);
28542         var b = this.stripBody;
28543         if(b.getWidth() > w){
28544             var tabs = this.items;
28545             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28546             if(availWidth < this.minTabWidth){
28547                 /*if(!this.sleft){    // incomplete scrolling code
28548                     this.createScrollButtons();
28549                 }
28550                 this.showScroll();
28551                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28552             }
28553         }else{
28554             if(this.currentTabWidth < this.preferredTabWidth){
28555                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28556             }
28557         }
28558     },
28559
28560     /**
28561      * Returns the number of tabs in this TabPanel.
28562      * @return {Number}
28563      */
28564      getCount : function(){
28565          return this.items.length;
28566      },
28567
28568     /**
28569      * Resizes all the tabs to the passed width
28570      * @param {Number} The new width
28571      */
28572     setTabWidth : function(width){
28573         this.currentTabWidth = width;
28574         for(var i = 0, len = this.items.length; i < len; i++) {
28575                 if(!this.items[i].isHidden()) {
28576                 this.items[i].setWidth(width);
28577             }
28578         }
28579     },
28580
28581     /**
28582      * Destroys this TabPanel
28583      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28584      */
28585     destroy : function(removeEl){
28586         Roo.EventManager.removeResizeListener(this.onResize, this);
28587         for(var i = 0, len = this.items.length; i < len; i++){
28588             this.items[i].purgeListeners();
28589         }
28590         if(removeEl === true){
28591             this.el.update("");
28592             this.el.remove();
28593         }
28594     }
28595 });
28596
28597 /**
28598  * @class Roo.TabPanelItem
28599  * @extends Roo.util.Observable
28600  * Represents an individual item (tab plus body) in a TabPanel.
28601  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28602  * @param {String} id The id of this TabPanelItem
28603  * @param {String} text The text for the tab of this TabPanelItem
28604  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28605  */
28606 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28607     /**
28608      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28609      * @type Roo.TabPanel
28610      */
28611     this.tabPanel = tabPanel;
28612     /**
28613      * The id for this TabPanelItem
28614      * @type String
28615      */
28616     this.id = id;
28617     /** @private */
28618     this.disabled = false;
28619     /** @private */
28620     this.text = text;
28621     /** @private */
28622     this.loaded = false;
28623     this.closable = closable;
28624
28625     /**
28626      * The body element for this TabPanelItem.
28627      * @type Roo.Element
28628      */
28629     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28630     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28631     this.bodyEl.setStyle("display", "block");
28632     this.bodyEl.setStyle("zoom", "1");
28633     this.hideAction();
28634
28635     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28636     /** @private */
28637     this.el = Roo.get(els.el, true);
28638     this.inner = Roo.get(els.inner, true);
28639     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28640     this.pnode = Roo.get(els.el.parentNode, true);
28641     this.el.on("mousedown", this.onTabMouseDown, this);
28642     this.el.on("click", this.onTabClick, this);
28643     /** @private */
28644     if(closable){
28645         var c = Roo.get(els.close, true);
28646         c.dom.title = this.closeText;
28647         c.addClassOnOver("close-over");
28648         c.on("click", this.closeClick, this);
28649      }
28650
28651     this.addEvents({
28652          /**
28653          * @event activate
28654          * Fires when this tab becomes the active tab.
28655          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28656          * @param {Roo.TabPanelItem} this
28657          */
28658         "activate": true,
28659         /**
28660          * @event beforeclose
28661          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28662          * @param {Roo.TabPanelItem} this
28663          * @param {Object} e Set cancel to true on this object to cancel the close.
28664          */
28665         "beforeclose": true,
28666         /**
28667          * @event close
28668          * Fires when this tab is closed.
28669          * @param {Roo.TabPanelItem} this
28670          */
28671          "close": true,
28672         /**
28673          * @event deactivate
28674          * Fires when this tab is no longer the active tab.
28675          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28676          * @param {Roo.TabPanelItem} this
28677          */
28678          "deactivate" : true
28679     });
28680     this.hidden = false;
28681
28682     Roo.TabPanelItem.superclass.constructor.call(this);
28683 };
28684
28685 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28686     purgeListeners : function(){
28687        Roo.util.Observable.prototype.purgeListeners.call(this);
28688        this.el.removeAllListeners();
28689     },
28690     /**
28691      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28692      */
28693     show : function(){
28694         this.pnode.addClass("on");
28695         this.showAction();
28696         if(Roo.isOpera){
28697             this.tabPanel.stripWrap.repaint();
28698         }
28699         this.fireEvent("activate", this.tabPanel, this);
28700     },
28701
28702     /**
28703      * Returns true if this tab is the active tab.
28704      * @return {Boolean}
28705      */
28706     isActive : function(){
28707         return this.tabPanel.getActiveTab() == this;
28708     },
28709
28710     /**
28711      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28712      */
28713     hide : function(){
28714         this.pnode.removeClass("on");
28715         this.hideAction();
28716         this.fireEvent("deactivate", this.tabPanel, this);
28717     },
28718
28719     hideAction : function(){
28720         this.bodyEl.hide();
28721         this.bodyEl.setStyle("position", "absolute");
28722         this.bodyEl.setLeft("-20000px");
28723         this.bodyEl.setTop("-20000px");
28724     },
28725
28726     showAction : function(){
28727         this.bodyEl.setStyle("position", "relative");
28728         this.bodyEl.setTop("");
28729         this.bodyEl.setLeft("");
28730         this.bodyEl.show();
28731     },
28732
28733     /**
28734      * Set the tooltip for the tab.
28735      * @param {String} tooltip The tab's tooltip
28736      */
28737     setTooltip : function(text){
28738         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28739             this.textEl.dom.qtip = text;
28740             this.textEl.dom.removeAttribute('title');
28741         }else{
28742             this.textEl.dom.title = text;
28743         }
28744     },
28745
28746     onTabClick : function(e){
28747         e.preventDefault();
28748         this.tabPanel.activate(this.id);
28749     },
28750
28751     onTabMouseDown : function(e){
28752         e.preventDefault();
28753         this.tabPanel.activate(this.id);
28754     },
28755
28756     getWidth : function(){
28757         return this.inner.getWidth();
28758     },
28759
28760     setWidth : function(width){
28761         var iwidth = width - this.pnode.getPadding("lr");
28762         this.inner.setWidth(iwidth);
28763         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28764         this.pnode.setWidth(width);
28765     },
28766
28767     /**
28768      * Show or hide the tab
28769      * @param {Boolean} hidden True to hide or false to show.
28770      */
28771     setHidden : function(hidden){
28772         this.hidden = hidden;
28773         this.pnode.setStyle("display", hidden ? "none" : "");
28774     },
28775
28776     /**
28777      * Returns true if this tab is "hidden"
28778      * @return {Boolean}
28779      */
28780     isHidden : function(){
28781         return this.hidden;
28782     },
28783
28784     /**
28785      * Returns the text for this tab
28786      * @return {String}
28787      */
28788     getText : function(){
28789         return this.text;
28790     },
28791
28792     autoSize : function(){
28793         //this.el.beginMeasure();
28794         this.textEl.setWidth(1);
28795         /*
28796          *  #2804 [new] Tabs in Roojs
28797          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28798          */
28799         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28800         //this.el.endMeasure();
28801     },
28802
28803     /**
28804      * Sets the text for the tab (Note: this also sets the tooltip text)
28805      * @param {String} text The tab's text and tooltip
28806      */
28807     setText : function(text){
28808         this.text = text;
28809         this.textEl.update(text);
28810         this.setTooltip(text);
28811         if(!this.tabPanel.resizeTabs){
28812             this.autoSize();
28813         }
28814     },
28815     /**
28816      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28817      */
28818     activate : function(){
28819         this.tabPanel.activate(this.id);
28820     },
28821
28822     /**
28823      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28824      */
28825     disable : function(){
28826         if(this.tabPanel.active != this){
28827             this.disabled = true;
28828             this.pnode.addClass("disabled");
28829         }
28830     },
28831
28832     /**
28833      * Enables this TabPanelItem if it was previously disabled.
28834      */
28835     enable : function(){
28836         this.disabled = false;
28837         this.pnode.removeClass("disabled");
28838     },
28839
28840     /**
28841      * Sets the content for this TabPanelItem.
28842      * @param {String} content The content
28843      * @param {Boolean} loadScripts true to look for and load scripts
28844      */
28845     setContent : function(content, loadScripts){
28846         this.bodyEl.update(content, loadScripts);
28847     },
28848
28849     /**
28850      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
28851      * @return {Roo.UpdateManager} The UpdateManager
28852      */
28853     getUpdateManager : function(){
28854         return this.bodyEl.getUpdateManager();
28855     },
28856
28857     /**
28858      * Set a URL to be used to load the content for this TabPanelItem.
28859      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
28860      * @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)
28861      * @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)
28862      * @return {Roo.UpdateManager} The UpdateManager
28863      */
28864     setUrl : function(url, params, loadOnce){
28865         if(this.refreshDelegate){
28866             this.un('activate', this.refreshDelegate);
28867         }
28868         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
28869         this.on("activate", this.refreshDelegate);
28870         return this.bodyEl.getUpdateManager();
28871     },
28872
28873     /** @private */
28874     _handleRefresh : function(url, params, loadOnce){
28875         if(!loadOnce || !this.loaded){
28876             var updater = this.bodyEl.getUpdateManager();
28877             updater.update(url, params, this._setLoaded.createDelegate(this));
28878         }
28879     },
28880
28881     /**
28882      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
28883      *   Will fail silently if the setUrl method has not been called.
28884      *   This does not activate the panel, just updates its content.
28885      */
28886     refresh : function(){
28887         if(this.refreshDelegate){
28888            this.loaded = false;
28889            this.refreshDelegate();
28890         }
28891     },
28892
28893     /** @private */
28894     _setLoaded : function(){
28895         this.loaded = true;
28896     },
28897
28898     /** @private */
28899     closeClick : function(e){
28900         var o = {};
28901         e.stopEvent();
28902         this.fireEvent("beforeclose", this, o);
28903         if(o.cancel !== true){
28904             this.tabPanel.removeTab(this.id);
28905         }
28906     },
28907     /**
28908      * The text displayed in the tooltip for the close icon.
28909      * @type String
28910      */
28911     closeText : "Close this tab"
28912 });
28913
28914 /** @private */
28915 Roo.TabPanel.prototype.createStrip = function(container){
28916     var strip = document.createElement("div");
28917     strip.className = "x-tabs-wrap";
28918     container.appendChild(strip);
28919     return strip;
28920 };
28921 /** @private */
28922 Roo.TabPanel.prototype.createStripList = function(strip){
28923     // div wrapper for retard IE
28924     // returns the "tr" element.
28925     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
28926         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
28927         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
28928     return strip.firstChild.firstChild.firstChild.firstChild;
28929 };
28930 /** @private */
28931 Roo.TabPanel.prototype.createBody = function(container){
28932     var body = document.createElement("div");
28933     Roo.id(body, "tab-body");
28934     Roo.fly(body).addClass("x-tabs-body");
28935     container.appendChild(body);
28936     return body;
28937 };
28938 /** @private */
28939 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
28940     var body = Roo.getDom(id);
28941     if(!body){
28942         body = document.createElement("div");
28943         body.id = id;
28944     }
28945     Roo.fly(body).addClass("x-tabs-item-body");
28946     bodyEl.insertBefore(body, bodyEl.firstChild);
28947     return body;
28948 };
28949 /** @private */
28950 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
28951     var td = document.createElement("td");
28952     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
28953     //stripEl.appendChild(td);
28954     if(closable){
28955         td.className = "x-tabs-closable";
28956         if(!this.closeTpl){
28957             this.closeTpl = new Roo.Template(
28958                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28959                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
28960                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
28961             );
28962         }
28963         var el = this.closeTpl.overwrite(td, {"text": text});
28964         var close = el.getElementsByTagName("div")[0];
28965         var inner = el.getElementsByTagName("em")[0];
28966         return {"el": el, "close": close, "inner": inner};
28967     } else {
28968         if(!this.tabTpl){
28969             this.tabTpl = new Roo.Template(
28970                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
28971                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
28972             );
28973         }
28974         var el = this.tabTpl.overwrite(td, {"text": text});
28975         var inner = el.getElementsByTagName("em")[0];
28976         return {"el": el, "inner": inner};
28977     }
28978 };/*
28979  * Based on:
28980  * Ext JS Library 1.1.1
28981  * Copyright(c) 2006-2007, Ext JS, LLC.
28982  *
28983  * Originally Released Under LGPL - original licence link has changed is not relivant.
28984  *
28985  * Fork - LGPL
28986  * <script type="text/javascript">
28987  */
28988
28989 /**
28990  * @class Roo.Button
28991  * @extends Roo.util.Observable
28992  * Simple Button class
28993  * @cfg {String} text The button text
28994  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
28995  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
28996  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
28997  * @cfg {Object} scope The scope of the handler
28998  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
28999  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29000  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29001  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29002  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29003  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29004    applies if enableToggle = true)
29005  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29006  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29007   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29008  * @constructor
29009  * Create a new button
29010  * @param {Object} config The config object
29011  */
29012 Roo.Button = function(renderTo, config)
29013 {
29014     if (!config) {
29015         config = renderTo;
29016         renderTo = config.renderTo || false;
29017     }
29018     
29019     Roo.apply(this, config);
29020     this.addEvents({
29021         /**
29022              * @event click
29023              * Fires when this button is clicked
29024              * @param {Button} this
29025              * @param {EventObject} e The click event
29026              */
29027             "click" : true,
29028         /**
29029              * @event toggle
29030              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29031              * @param {Button} this
29032              * @param {Boolean} pressed
29033              */
29034             "toggle" : true,
29035         /**
29036              * @event mouseover
29037              * Fires when the mouse hovers over the button
29038              * @param {Button} this
29039              * @param {Event} e The event object
29040              */
29041         'mouseover' : true,
29042         /**
29043              * @event mouseout
29044              * Fires when the mouse exits the button
29045              * @param {Button} this
29046              * @param {Event} e The event object
29047              */
29048         'mouseout': true,
29049          /**
29050              * @event render
29051              * Fires when the button is rendered
29052              * @param {Button} this
29053              */
29054         'render': true
29055     });
29056     if(this.menu){
29057         this.menu = Roo.menu.MenuMgr.get(this.menu);
29058     }
29059     // register listeners first!!  - so render can be captured..
29060     Roo.util.Observable.call(this);
29061     if(renderTo){
29062         this.render(renderTo);
29063     }
29064     
29065   
29066 };
29067
29068 Roo.extend(Roo.Button, Roo.util.Observable, {
29069     /**
29070      * 
29071      */
29072     
29073     /**
29074      * Read-only. True if this button is hidden
29075      * @type Boolean
29076      */
29077     hidden : false,
29078     /**
29079      * Read-only. True if this button is disabled
29080      * @type Boolean
29081      */
29082     disabled : false,
29083     /**
29084      * Read-only. True if this button is pressed (only if enableToggle = true)
29085      * @type Boolean
29086      */
29087     pressed : false,
29088
29089     /**
29090      * @cfg {Number} tabIndex 
29091      * The DOM tabIndex for this button (defaults to undefined)
29092      */
29093     tabIndex : undefined,
29094
29095     /**
29096      * @cfg {Boolean} enableToggle
29097      * True to enable pressed/not pressed toggling (defaults to false)
29098      */
29099     enableToggle: false,
29100     /**
29101      * @cfg {Mixed} menu
29102      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29103      */
29104     menu : undefined,
29105     /**
29106      * @cfg {String} menuAlign
29107      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29108      */
29109     menuAlign : "tl-bl?",
29110
29111     /**
29112      * @cfg {String} iconCls
29113      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29114      */
29115     iconCls : undefined,
29116     /**
29117      * @cfg {String} type
29118      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29119      */
29120     type : 'button',
29121
29122     // private
29123     menuClassTarget: 'tr',
29124
29125     /**
29126      * @cfg {String} clickEvent
29127      * The type of event to map to the button's event handler (defaults to 'click')
29128      */
29129     clickEvent : 'click',
29130
29131     /**
29132      * @cfg {Boolean} handleMouseEvents
29133      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29134      */
29135     handleMouseEvents : true,
29136
29137     /**
29138      * @cfg {String} tooltipType
29139      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29140      */
29141     tooltipType : 'qtip',
29142
29143     /**
29144      * @cfg {String} cls
29145      * A CSS class to apply to the button's main element.
29146      */
29147     
29148     /**
29149      * @cfg {Roo.Template} template (Optional)
29150      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29151      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29152      * require code modifications if required elements (e.g. a button) aren't present.
29153      */
29154
29155     // private
29156     render : function(renderTo){
29157         var btn;
29158         if(this.hideParent){
29159             this.parentEl = Roo.get(renderTo);
29160         }
29161         if(!this.dhconfig){
29162             if(!this.template){
29163                 if(!Roo.Button.buttonTemplate){
29164                     // hideous table template
29165                     Roo.Button.buttonTemplate = new Roo.Template(
29166                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29167                         '<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>',
29168                         "</tr></tbody></table>");
29169                 }
29170                 this.template = Roo.Button.buttonTemplate;
29171             }
29172             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29173             var btnEl = btn.child("button:first");
29174             btnEl.on('focus', this.onFocus, this);
29175             btnEl.on('blur', this.onBlur, this);
29176             if(this.cls){
29177                 btn.addClass(this.cls);
29178             }
29179             if(this.icon){
29180                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29181             }
29182             if(this.iconCls){
29183                 btnEl.addClass(this.iconCls);
29184                 if(!this.cls){
29185                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29186                 }
29187             }
29188             if(this.tabIndex !== undefined){
29189                 btnEl.dom.tabIndex = this.tabIndex;
29190             }
29191             if(this.tooltip){
29192                 if(typeof this.tooltip == 'object'){
29193                     Roo.QuickTips.tips(Roo.apply({
29194                           target: btnEl.id
29195                     }, this.tooltip));
29196                 } else {
29197                     btnEl.dom[this.tooltipType] = this.tooltip;
29198                 }
29199             }
29200         }else{
29201             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29202         }
29203         this.el = btn;
29204         if(this.id){
29205             this.el.dom.id = this.el.id = this.id;
29206         }
29207         if(this.menu){
29208             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29209             this.menu.on("show", this.onMenuShow, this);
29210             this.menu.on("hide", this.onMenuHide, this);
29211         }
29212         btn.addClass("x-btn");
29213         if(Roo.isIE && !Roo.isIE7){
29214             this.autoWidth.defer(1, this);
29215         }else{
29216             this.autoWidth();
29217         }
29218         if(this.handleMouseEvents){
29219             btn.on("mouseover", this.onMouseOver, this);
29220             btn.on("mouseout", this.onMouseOut, this);
29221             btn.on("mousedown", this.onMouseDown, this);
29222         }
29223         btn.on(this.clickEvent, this.onClick, this);
29224         //btn.on("mouseup", this.onMouseUp, this);
29225         if(this.hidden){
29226             this.hide();
29227         }
29228         if(this.disabled){
29229             this.disable();
29230         }
29231         Roo.ButtonToggleMgr.register(this);
29232         if(this.pressed){
29233             this.el.addClass("x-btn-pressed");
29234         }
29235         if(this.repeat){
29236             var repeater = new Roo.util.ClickRepeater(btn,
29237                 typeof this.repeat == "object" ? this.repeat : {}
29238             );
29239             repeater.on("click", this.onClick,  this);
29240         }
29241         
29242         this.fireEvent('render', this);
29243         
29244     },
29245     /**
29246      * Returns the button's underlying element
29247      * @return {Roo.Element} The element
29248      */
29249     getEl : function(){
29250         return this.el;  
29251     },
29252     
29253     /**
29254      * Destroys this Button and removes any listeners.
29255      */
29256     destroy : function(){
29257         Roo.ButtonToggleMgr.unregister(this);
29258         this.el.removeAllListeners();
29259         this.purgeListeners();
29260         this.el.remove();
29261     },
29262
29263     // private
29264     autoWidth : function(){
29265         if(this.el){
29266             this.el.setWidth("auto");
29267             if(Roo.isIE7 && Roo.isStrict){
29268                 var ib = this.el.child('button');
29269                 if(ib && ib.getWidth() > 20){
29270                     ib.clip();
29271                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29272                 }
29273             }
29274             if(this.minWidth){
29275                 if(this.hidden){
29276                     this.el.beginMeasure();
29277                 }
29278                 if(this.el.getWidth() < this.minWidth){
29279                     this.el.setWidth(this.minWidth);
29280                 }
29281                 if(this.hidden){
29282                     this.el.endMeasure();
29283                 }
29284             }
29285         }
29286     },
29287
29288     /**
29289      * Assigns this button's click handler
29290      * @param {Function} handler The function to call when the button is clicked
29291      * @param {Object} scope (optional) Scope for the function passed in
29292      */
29293     setHandler : function(handler, scope){
29294         this.handler = handler;
29295         this.scope = scope;  
29296     },
29297     
29298     /**
29299      * Sets this button's text
29300      * @param {String} text The button text
29301      */
29302     setText : function(text){
29303         this.text = text;
29304         if(this.el){
29305             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29306         }
29307         this.autoWidth();
29308     },
29309     
29310     /**
29311      * Gets the text for this button
29312      * @return {String} The button text
29313      */
29314     getText : function(){
29315         return this.text;  
29316     },
29317     
29318     /**
29319      * Show this button
29320      */
29321     show: function(){
29322         this.hidden = false;
29323         if(this.el){
29324             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29325         }
29326     },
29327     
29328     /**
29329      * Hide this button
29330      */
29331     hide: function(){
29332         this.hidden = true;
29333         if(this.el){
29334             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29335         }
29336     },
29337     
29338     /**
29339      * Convenience function for boolean show/hide
29340      * @param {Boolean} visible True to show, false to hide
29341      */
29342     setVisible: function(visible){
29343         if(visible) {
29344             this.show();
29345         }else{
29346             this.hide();
29347         }
29348     },
29349     
29350     /**
29351      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29352      * @param {Boolean} state (optional) Force a particular state
29353      */
29354     toggle : function(state){
29355         state = state === undefined ? !this.pressed : state;
29356         if(state != this.pressed){
29357             if(state){
29358                 this.el.addClass("x-btn-pressed");
29359                 this.pressed = true;
29360                 this.fireEvent("toggle", this, true);
29361             }else{
29362                 this.el.removeClass("x-btn-pressed");
29363                 this.pressed = false;
29364                 this.fireEvent("toggle", this, false);
29365             }
29366             if(this.toggleHandler){
29367                 this.toggleHandler.call(this.scope || this, this, state);
29368             }
29369         }
29370     },
29371     
29372     /**
29373      * Focus the button
29374      */
29375     focus : function(){
29376         this.el.child('button:first').focus();
29377     },
29378     
29379     /**
29380      * Disable this button
29381      */
29382     disable : function(){
29383         if(this.el){
29384             this.el.addClass("x-btn-disabled");
29385         }
29386         this.disabled = true;
29387     },
29388     
29389     /**
29390      * Enable this button
29391      */
29392     enable : function(){
29393         if(this.el){
29394             this.el.removeClass("x-btn-disabled");
29395         }
29396         this.disabled = false;
29397     },
29398
29399     /**
29400      * Convenience function for boolean enable/disable
29401      * @param {Boolean} enabled True to enable, false to disable
29402      */
29403     setDisabled : function(v){
29404         this[v !== true ? "enable" : "disable"]();
29405     },
29406
29407     // private
29408     onClick : function(e)
29409     {
29410         if(e){
29411             e.preventDefault();
29412         }
29413         if(e.button != 0){
29414             return;
29415         }
29416         if(!this.disabled){
29417             if(this.enableToggle){
29418                 this.toggle();
29419             }
29420             if(this.menu && !this.menu.isVisible()){
29421                 this.menu.show(this.el, this.menuAlign);
29422             }
29423             this.fireEvent("click", this, e);
29424             if(this.handler){
29425                 this.el.removeClass("x-btn-over");
29426                 this.handler.call(this.scope || this, this, e);
29427             }
29428         }
29429     },
29430     // private
29431     onMouseOver : function(e){
29432         if(!this.disabled){
29433             this.el.addClass("x-btn-over");
29434             this.fireEvent('mouseover', this, e);
29435         }
29436     },
29437     // private
29438     onMouseOut : function(e){
29439         if(!e.within(this.el,  true)){
29440             this.el.removeClass("x-btn-over");
29441             this.fireEvent('mouseout', this, e);
29442         }
29443     },
29444     // private
29445     onFocus : function(e){
29446         if(!this.disabled){
29447             this.el.addClass("x-btn-focus");
29448         }
29449     },
29450     // private
29451     onBlur : function(e){
29452         this.el.removeClass("x-btn-focus");
29453     },
29454     // private
29455     onMouseDown : function(e){
29456         if(!this.disabled && e.button == 0){
29457             this.el.addClass("x-btn-click");
29458             Roo.get(document).on('mouseup', this.onMouseUp, this);
29459         }
29460     },
29461     // private
29462     onMouseUp : function(e){
29463         if(e.button == 0){
29464             this.el.removeClass("x-btn-click");
29465             Roo.get(document).un('mouseup', this.onMouseUp, this);
29466         }
29467     },
29468     // private
29469     onMenuShow : function(e){
29470         this.el.addClass("x-btn-menu-active");
29471     },
29472     // private
29473     onMenuHide : function(e){
29474         this.el.removeClass("x-btn-menu-active");
29475     }   
29476 });
29477
29478 // Private utility class used by Button
29479 Roo.ButtonToggleMgr = function(){
29480    var groups = {};
29481    
29482    function toggleGroup(btn, state){
29483        if(state){
29484            var g = groups[btn.toggleGroup];
29485            for(var i = 0, l = g.length; i < l; i++){
29486                if(g[i] != btn){
29487                    g[i].toggle(false);
29488                }
29489            }
29490        }
29491    }
29492    
29493    return {
29494        register : function(btn){
29495            if(!btn.toggleGroup){
29496                return;
29497            }
29498            var g = groups[btn.toggleGroup];
29499            if(!g){
29500                g = groups[btn.toggleGroup] = [];
29501            }
29502            g.push(btn);
29503            btn.on("toggle", toggleGroup);
29504        },
29505        
29506        unregister : function(btn){
29507            if(!btn.toggleGroup){
29508                return;
29509            }
29510            var g = groups[btn.toggleGroup];
29511            if(g){
29512                g.remove(btn);
29513                btn.un("toggle", toggleGroup);
29514            }
29515        }
29516    };
29517 }();/*
29518  * Based on:
29519  * Ext JS Library 1.1.1
29520  * Copyright(c) 2006-2007, Ext JS, LLC.
29521  *
29522  * Originally Released Under LGPL - original licence link has changed is not relivant.
29523  *
29524  * Fork - LGPL
29525  * <script type="text/javascript">
29526  */
29527  
29528 /**
29529  * @class Roo.SplitButton
29530  * @extends Roo.Button
29531  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29532  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29533  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29534  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29535  * @cfg {String} arrowTooltip The title attribute of the arrow
29536  * @constructor
29537  * Create a new menu button
29538  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29539  * @param {Object} config The config object
29540  */
29541 Roo.SplitButton = function(renderTo, config){
29542     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29543     /**
29544      * @event arrowclick
29545      * Fires when this button's arrow is clicked
29546      * @param {SplitButton} this
29547      * @param {EventObject} e The click event
29548      */
29549     this.addEvents({"arrowclick":true});
29550 };
29551
29552 Roo.extend(Roo.SplitButton, Roo.Button, {
29553     render : function(renderTo){
29554         // this is one sweet looking template!
29555         var tpl = new Roo.Template(
29556             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29557             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29558             '<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>',
29559             "</tbody></table></td><td>",
29560             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29561             '<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>',
29562             "</tbody></table></td></tr></table>"
29563         );
29564         var btn = tpl.append(renderTo, [this.text, this.type], true);
29565         var btnEl = btn.child("button");
29566         if(this.cls){
29567             btn.addClass(this.cls);
29568         }
29569         if(this.icon){
29570             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29571         }
29572         if(this.iconCls){
29573             btnEl.addClass(this.iconCls);
29574             if(!this.cls){
29575                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29576             }
29577         }
29578         this.el = btn;
29579         if(this.handleMouseEvents){
29580             btn.on("mouseover", this.onMouseOver, this);
29581             btn.on("mouseout", this.onMouseOut, this);
29582             btn.on("mousedown", this.onMouseDown, this);
29583             btn.on("mouseup", this.onMouseUp, this);
29584         }
29585         btn.on(this.clickEvent, this.onClick, this);
29586         if(this.tooltip){
29587             if(typeof this.tooltip == 'object'){
29588                 Roo.QuickTips.tips(Roo.apply({
29589                       target: btnEl.id
29590                 }, this.tooltip));
29591             } else {
29592                 btnEl.dom[this.tooltipType] = this.tooltip;
29593             }
29594         }
29595         if(this.arrowTooltip){
29596             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29597         }
29598         if(this.hidden){
29599             this.hide();
29600         }
29601         if(this.disabled){
29602             this.disable();
29603         }
29604         if(this.pressed){
29605             this.el.addClass("x-btn-pressed");
29606         }
29607         if(Roo.isIE && !Roo.isIE7){
29608             this.autoWidth.defer(1, this);
29609         }else{
29610             this.autoWidth();
29611         }
29612         if(this.menu){
29613             this.menu.on("show", this.onMenuShow, this);
29614             this.menu.on("hide", this.onMenuHide, this);
29615         }
29616         this.fireEvent('render', this);
29617     },
29618
29619     // private
29620     autoWidth : function(){
29621         if(this.el){
29622             var tbl = this.el.child("table:first");
29623             var tbl2 = this.el.child("table:last");
29624             this.el.setWidth("auto");
29625             tbl.setWidth("auto");
29626             if(Roo.isIE7 && Roo.isStrict){
29627                 var ib = this.el.child('button:first');
29628                 if(ib && ib.getWidth() > 20){
29629                     ib.clip();
29630                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29631                 }
29632             }
29633             if(this.minWidth){
29634                 if(this.hidden){
29635                     this.el.beginMeasure();
29636                 }
29637                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29638                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29639                 }
29640                 if(this.hidden){
29641                     this.el.endMeasure();
29642                 }
29643             }
29644             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29645         } 
29646     },
29647     /**
29648      * Sets this button's click handler
29649      * @param {Function} handler The function to call when the button is clicked
29650      * @param {Object} scope (optional) Scope for the function passed above
29651      */
29652     setHandler : function(handler, scope){
29653         this.handler = handler;
29654         this.scope = scope;  
29655     },
29656     
29657     /**
29658      * Sets this button's arrow click handler
29659      * @param {Function} handler The function to call when the arrow is clicked
29660      * @param {Object} scope (optional) Scope for the function passed above
29661      */
29662     setArrowHandler : function(handler, scope){
29663         this.arrowHandler = handler;
29664         this.scope = scope;  
29665     },
29666     
29667     /**
29668      * Focus the button
29669      */
29670     focus : function(){
29671         if(this.el){
29672             this.el.child("button:first").focus();
29673         }
29674     },
29675
29676     // private
29677     onClick : function(e){
29678         e.preventDefault();
29679         if(!this.disabled){
29680             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29681                 if(this.menu && !this.menu.isVisible()){
29682                     this.menu.show(this.el, this.menuAlign);
29683                 }
29684                 this.fireEvent("arrowclick", this, e);
29685                 if(this.arrowHandler){
29686                     this.arrowHandler.call(this.scope || this, this, e);
29687                 }
29688             }else{
29689                 this.fireEvent("click", this, e);
29690                 if(this.handler){
29691                     this.handler.call(this.scope || this, this, e);
29692                 }
29693             }
29694         }
29695     },
29696     // private
29697     onMouseDown : function(e){
29698         if(!this.disabled){
29699             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29700         }
29701     },
29702     // private
29703     onMouseUp : function(e){
29704         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29705     }   
29706 });
29707
29708
29709 // backwards compat
29710 Roo.MenuButton = Roo.SplitButton;/*
29711  * Based on:
29712  * Ext JS Library 1.1.1
29713  * Copyright(c) 2006-2007, Ext JS, LLC.
29714  *
29715  * Originally Released Under LGPL - original licence link has changed is not relivant.
29716  *
29717  * Fork - LGPL
29718  * <script type="text/javascript">
29719  */
29720
29721 /**
29722  * @class Roo.Toolbar
29723  * Basic Toolbar class.
29724  * @constructor
29725  * Creates a new Toolbar
29726  * @param {Object} container The config object
29727  */ 
29728 Roo.Toolbar = function(container, buttons, config)
29729 {
29730     /// old consturctor format still supported..
29731     if(container instanceof Array){ // omit the container for later rendering
29732         buttons = container;
29733         config = buttons;
29734         container = null;
29735     }
29736     if (typeof(container) == 'object' && container.xtype) {
29737         config = container;
29738         container = config.container;
29739         buttons = config.buttons || []; // not really - use items!!
29740     }
29741     var xitems = [];
29742     if (config && config.items) {
29743         xitems = config.items;
29744         delete config.items;
29745     }
29746     Roo.apply(this, config);
29747     this.buttons = buttons;
29748     
29749     if(container){
29750         this.render(container);
29751     }
29752     this.xitems = xitems;
29753     Roo.each(xitems, function(b) {
29754         this.add(b);
29755     }, this);
29756     
29757 };
29758
29759 Roo.Toolbar.prototype = {
29760     /**
29761      * @cfg {Array} items
29762      * array of button configs or elements to add (will be converted to a MixedCollection)
29763      */
29764     
29765     /**
29766      * @cfg {String/HTMLElement/Element} container
29767      * The id or element that will contain the toolbar
29768      */
29769     // private
29770     render : function(ct){
29771         this.el = Roo.get(ct);
29772         if(this.cls){
29773             this.el.addClass(this.cls);
29774         }
29775         // using a table allows for vertical alignment
29776         // 100% width is needed by Safari...
29777         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29778         this.tr = this.el.child("tr", true);
29779         var autoId = 0;
29780         this.items = new Roo.util.MixedCollection(false, function(o){
29781             return o.id || ("item" + (++autoId));
29782         });
29783         if(this.buttons){
29784             this.add.apply(this, this.buttons);
29785             delete this.buttons;
29786         }
29787     },
29788
29789     /**
29790      * Adds element(s) to the toolbar -- this function takes a variable number of 
29791      * arguments of mixed type and adds them to the toolbar.
29792      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29793      * <ul>
29794      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29795      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29796      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29797      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29798      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29799      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29800      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29801      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29802      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29803      * </ul>
29804      * @param {Mixed} arg2
29805      * @param {Mixed} etc.
29806      */
29807     add : function(){
29808         var a = arguments, l = a.length;
29809         for(var i = 0; i < l; i++){
29810             this._add(a[i]);
29811         }
29812     },
29813     // private..
29814     _add : function(el) {
29815         
29816         if (el.xtype) {
29817             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29818         }
29819         
29820         if (el.applyTo){ // some kind of form field
29821             return this.addField(el);
29822         } 
29823         if (el.render){ // some kind of Toolbar.Item
29824             return this.addItem(el);
29825         }
29826         if (typeof el == "string"){ // string
29827             if(el == "separator" || el == "-"){
29828                 return this.addSeparator();
29829             }
29830             if (el == " "){
29831                 return this.addSpacer();
29832             }
29833             if(el == "->"){
29834                 return this.addFill();
29835             }
29836             return this.addText(el);
29837             
29838         }
29839         if(el.tagName){ // element
29840             return this.addElement(el);
29841         }
29842         if(typeof el == "object"){ // must be button config?
29843             return this.addButton(el);
29844         }
29845         // and now what?!?!
29846         return false;
29847         
29848     },
29849     
29850     /**
29851      * Add an Xtype element
29852      * @param {Object} xtype Xtype Object
29853      * @return {Object} created Object
29854      */
29855     addxtype : function(e){
29856         return this.add(e);  
29857     },
29858     
29859     /**
29860      * Returns the Element for this toolbar.
29861      * @return {Roo.Element}
29862      */
29863     getEl : function(){
29864         return this.el;  
29865     },
29866     
29867     /**
29868      * Adds a separator
29869      * @return {Roo.Toolbar.Item} The separator item
29870      */
29871     addSeparator : function(){
29872         return this.addItem(new Roo.Toolbar.Separator());
29873     },
29874
29875     /**
29876      * Adds a spacer element
29877      * @return {Roo.Toolbar.Spacer} The spacer item
29878      */
29879     addSpacer : function(){
29880         return this.addItem(new Roo.Toolbar.Spacer());
29881     },
29882
29883     /**
29884      * Adds a fill element that forces subsequent additions to the right side of the toolbar
29885      * @return {Roo.Toolbar.Fill} The fill item
29886      */
29887     addFill : function(){
29888         return this.addItem(new Roo.Toolbar.Fill());
29889     },
29890
29891     /**
29892      * Adds any standard HTML element to the toolbar
29893      * @param {String/HTMLElement/Element} el The element or id of the element to add
29894      * @return {Roo.Toolbar.Item} The element's item
29895      */
29896     addElement : function(el){
29897         return this.addItem(new Roo.Toolbar.Item(el));
29898     },
29899     /**
29900      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
29901      * @type Roo.util.MixedCollection  
29902      */
29903     items : false,
29904      
29905     /**
29906      * Adds any Toolbar.Item or subclass
29907      * @param {Roo.Toolbar.Item} item
29908      * @return {Roo.Toolbar.Item} The item
29909      */
29910     addItem : function(item){
29911         var td = this.nextBlock();
29912         item.render(td);
29913         this.items.add(item);
29914         return item;
29915     },
29916     
29917     /**
29918      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
29919      * @param {Object/Array} config A button config or array of configs
29920      * @return {Roo.Toolbar.Button/Array}
29921      */
29922     addButton : function(config){
29923         if(config instanceof Array){
29924             var buttons = [];
29925             for(var i = 0, len = config.length; i < len; i++) {
29926                 buttons.push(this.addButton(config[i]));
29927             }
29928             return buttons;
29929         }
29930         var b = config;
29931         if(!(config instanceof Roo.Toolbar.Button)){
29932             b = config.split ?
29933                 new Roo.Toolbar.SplitButton(config) :
29934                 new Roo.Toolbar.Button(config);
29935         }
29936         var td = this.nextBlock();
29937         b.render(td);
29938         this.items.add(b);
29939         return b;
29940     },
29941     
29942     /**
29943      * Adds text to the toolbar
29944      * @param {String} text The text to add
29945      * @return {Roo.Toolbar.Item} The element's item
29946      */
29947     addText : function(text){
29948         return this.addItem(new Roo.Toolbar.TextItem(text));
29949     },
29950     
29951     /**
29952      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
29953      * @param {Number} index The index where the item is to be inserted
29954      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
29955      * @return {Roo.Toolbar.Button/Item}
29956      */
29957     insertButton : function(index, item){
29958         if(item instanceof Array){
29959             var buttons = [];
29960             for(var i = 0, len = item.length; i < len; i++) {
29961                buttons.push(this.insertButton(index + i, item[i]));
29962             }
29963             return buttons;
29964         }
29965         if (!(item instanceof Roo.Toolbar.Button)){
29966            item = new Roo.Toolbar.Button(item);
29967         }
29968         var td = document.createElement("td");
29969         this.tr.insertBefore(td, this.tr.childNodes[index]);
29970         item.render(td);
29971         this.items.insert(index, item);
29972         return item;
29973     },
29974     
29975     /**
29976      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
29977      * @param {Object} config
29978      * @return {Roo.Toolbar.Item} The element's item
29979      */
29980     addDom : function(config, returnEl){
29981         var td = this.nextBlock();
29982         Roo.DomHelper.overwrite(td, config);
29983         var ti = new Roo.Toolbar.Item(td.firstChild);
29984         ti.render(td);
29985         this.items.add(ti);
29986         return ti;
29987     },
29988
29989     /**
29990      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
29991      * @type Roo.util.MixedCollection  
29992      */
29993     fields : false,
29994     
29995     /**
29996      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
29997      * Note: the field should not have been rendered yet. For a field that has already been
29998      * rendered, use {@link #addElement}.
29999      * @param {Roo.form.Field} field
30000      * @return {Roo.ToolbarItem}
30001      */
30002      
30003       
30004     addField : function(field) {
30005         if (!this.fields) {
30006             var autoId = 0;
30007             this.fields = new Roo.util.MixedCollection(false, function(o){
30008                 return o.id || ("item" + (++autoId));
30009             });
30010
30011         }
30012         
30013         var td = this.nextBlock();
30014         field.render(td);
30015         var ti = new Roo.Toolbar.Item(td.firstChild);
30016         ti.render(td);
30017         this.items.add(ti);
30018         this.fields.add(field);
30019         return ti;
30020     },
30021     /**
30022      * Hide the toolbar
30023      * @method hide
30024      */
30025      
30026       
30027     hide : function()
30028     {
30029         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30030         this.el.child('div').hide();
30031     },
30032     /**
30033      * Show the toolbar
30034      * @method show
30035      */
30036     show : function()
30037     {
30038         this.el.child('div').show();
30039     },
30040       
30041     // private
30042     nextBlock : function(){
30043         var td = document.createElement("td");
30044         this.tr.appendChild(td);
30045         return td;
30046     },
30047
30048     // private
30049     destroy : function(){
30050         if(this.items){ // rendered?
30051             Roo.destroy.apply(Roo, this.items.items);
30052         }
30053         if(this.fields){ // rendered?
30054             Roo.destroy.apply(Roo, this.fields.items);
30055         }
30056         Roo.Element.uncache(this.el, this.tr);
30057     }
30058 };
30059
30060 /**
30061  * @class Roo.Toolbar.Item
30062  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30063  * @constructor
30064  * Creates a new Item
30065  * @param {HTMLElement} el 
30066  */
30067 Roo.Toolbar.Item = function(el){
30068     var cfg = {};
30069     if (typeof (el.xtype) != 'undefined') {
30070         cfg = el;
30071         el = cfg.el;
30072     }
30073     
30074     this.el = Roo.getDom(el);
30075     this.id = Roo.id(this.el);
30076     this.hidden = false;
30077     
30078     this.addEvents({
30079          /**
30080              * @event render
30081              * Fires when the button is rendered
30082              * @param {Button} this
30083              */
30084         'render': true
30085     });
30086     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30087 };
30088 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30089 //Roo.Toolbar.Item.prototype = {
30090     
30091     /**
30092      * Get this item's HTML Element
30093      * @return {HTMLElement}
30094      */
30095     getEl : function(){
30096        return this.el;  
30097     },
30098
30099     // private
30100     render : function(td){
30101         
30102          this.td = td;
30103         td.appendChild(this.el);
30104         
30105         this.fireEvent('render', this);
30106     },
30107     
30108     /**
30109      * Removes and destroys this item.
30110      */
30111     destroy : function(){
30112         this.td.parentNode.removeChild(this.td);
30113     },
30114     
30115     /**
30116      * Shows this item.
30117      */
30118     show: function(){
30119         this.hidden = false;
30120         this.td.style.display = "";
30121     },
30122     
30123     /**
30124      * Hides this item.
30125      */
30126     hide: function(){
30127         this.hidden = true;
30128         this.td.style.display = "none";
30129     },
30130     
30131     /**
30132      * Convenience function for boolean show/hide.
30133      * @param {Boolean} visible true to show/false to hide
30134      */
30135     setVisible: function(visible){
30136         if(visible) {
30137             this.show();
30138         }else{
30139             this.hide();
30140         }
30141     },
30142     
30143     /**
30144      * Try to focus this item.
30145      */
30146     focus : function(){
30147         Roo.fly(this.el).focus();
30148     },
30149     
30150     /**
30151      * Disables this item.
30152      */
30153     disable : function(){
30154         Roo.fly(this.td).addClass("x-item-disabled");
30155         this.disabled = true;
30156         this.el.disabled = true;
30157     },
30158     
30159     /**
30160      * Enables this item.
30161      */
30162     enable : function(){
30163         Roo.fly(this.td).removeClass("x-item-disabled");
30164         this.disabled = false;
30165         this.el.disabled = false;
30166     }
30167 });
30168
30169
30170 /**
30171  * @class Roo.Toolbar.Separator
30172  * @extends Roo.Toolbar.Item
30173  * A simple toolbar separator class
30174  * @constructor
30175  * Creates a new Separator
30176  */
30177 Roo.Toolbar.Separator = function(cfg){
30178     
30179     var s = document.createElement("span");
30180     s.className = "ytb-sep";
30181     if (cfg) {
30182         cfg.el = s;
30183     }
30184     
30185     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30186 };
30187 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30188     enable:Roo.emptyFn,
30189     disable:Roo.emptyFn,
30190     focus:Roo.emptyFn
30191 });
30192
30193 /**
30194  * @class Roo.Toolbar.Spacer
30195  * @extends Roo.Toolbar.Item
30196  * A simple element that adds extra horizontal space to a toolbar.
30197  * @constructor
30198  * Creates a new Spacer
30199  */
30200 Roo.Toolbar.Spacer = function(cfg){
30201     var s = document.createElement("div");
30202     s.className = "ytb-spacer";
30203     if (cfg) {
30204         cfg.el = s;
30205     }
30206     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30207 };
30208 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30209     enable:Roo.emptyFn,
30210     disable:Roo.emptyFn,
30211     focus:Roo.emptyFn
30212 });
30213
30214 /**
30215  * @class Roo.Toolbar.Fill
30216  * @extends Roo.Toolbar.Spacer
30217  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30218  * @constructor
30219  * Creates a new Spacer
30220  */
30221 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30222     // private
30223     render : function(td){
30224         td.style.width = '100%';
30225         Roo.Toolbar.Fill.superclass.render.call(this, td);
30226     }
30227 });
30228
30229 /**
30230  * @class Roo.Toolbar.TextItem
30231  * @extends Roo.Toolbar.Item
30232  * A simple class that renders text directly into a toolbar.
30233  * @constructor
30234  * Creates a new TextItem
30235  * @param {String} text
30236  */
30237 Roo.Toolbar.TextItem = function(cfg){
30238     var  text = cfg || "";
30239     if (typeof(cfg) == 'object') {
30240         text = cfg.text || "";
30241     }  else {
30242         cfg = null;
30243     }
30244     var s = document.createElement("span");
30245     s.className = "ytb-text";
30246     s.innerHTML = text;
30247     if (cfg) {
30248         cfg.el  = s;
30249     }
30250     
30251     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30252 };
30253 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30254     
30255      
30256     enable:Roo.emptyFn,
30257     disable:Roo.emptyFn,
30258     focus:Roo.emptyFn
30259 });
30260
30261 /**
30262  * @class Roo.Toolbar.Button
30263  * @extends Roo.Button
30264  * A button that renders into a toolbar.
30265  * @constructor
30266  * Creates a new Button
30267  * @param {Object} config A standard {@link Roo.Button} config object
30268  */
30269 Roo.Toolbar.Button = function(config){
30270     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30271 };
30272 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30273     render : function(td){
30274         this.td = td;
30275         Roo.Toolbar.Button.superclass.render.call(this, td);
30276     },
30277     
30278     /**
30279      * Removes and destroys this button
30280      */
30281     destroy : function(){
30282         Roo.Toolbar.Button.superclass.destroy.call(this);
30283         this.td.parentNode.removeChild(this.td);
30284     },
30285     
30286     /**
30287      * Shows this button
30288      */
30289     show: function(){
30290         this.hidden = false;
30291         this.td.style.display = "";
30292     },
30293     
30294     /**
30295      * Hides this button
30296      */
30297     hide: function(){
30298         this.hidden = true;
30299         this.td.style.display = "none";
30300     },
30301
30302     /**
30303      * Disables this item
30304      */
30305     disable : function(){
30306         Roo.fly(this.td).addClass("x-item-disabled");
30307         this.disabled = true;
30308     },
30309
30310     /**
30311      * Enables this item
30312      */
30313     enable : function(){
30314         Roo.fly(this.td).removeClass("x-item-disabled");
30315         this.disabled = false;
30316     }
30317 });
30318 // backwards compat
30319 Roo.ToolbarButton = Roo.Toolbar.Button;
30320
30321 /**
30322  * @class Roo.Toolbar.SplitButton
30323  * @extends Roo.SplitButton
30324  * A menu button that renders into a toolbar.
30325  * @constructor
30326  * Creates a new SplitButton
30327  * @param {Object} config A standard {@link Roo.SplitButton} config object
30328  */
30329 Roo.Toolbar.SplitButton = function(config){
30330     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30331 };
30332 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30333     render : function(td){
30334         this.td = td;
30335         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30336     },
30337     
30338     /**
30339      * Removes and destroys this button
30340      */
30341     destroy : function(){
30342         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30343         this.td.parentNode.removeChild(this.td);
30344     },
30345     
30346     /**
30347      * Shows this button
30348      */
30349     show: function(){
30350         this.hidden = false;
30351         this.td.style.display = "";
30352     },
30353     
30354     /**
30355      * Hides this button
30356      */
30357     hide: function(){
30358         this.hidden = true;
30359         this.td.style.display = "none";
30360     }
30361 });
30362
30363 // backwards compat
30364 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30365  * Based on:
30366  * Ext JS Library 1.1.1
30367  * Copyright(c) 2006-2007, Ext JS, LLC.
30368  *
30369  * Originally Released Under LGPL - original licence link has changed is not relivant.
30370  *
30371  * Fork - LGPL
30372  * <script type="text/javascript">
30373  */
30374  
30375 /**
30376  * @class Roo.PagingToolbar
30377  * @extends Roo.Toolbar
30378  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30379  * @constructor
30380  * Create a new PagingToolbar
30381  * @param {Object} config The config object
30382  */
30383 Roo.PagingToolbar = function(el, ds, config)
30384 {
30385     // old args format still supported... - xtype is prefered..
30386     if (typeof(el) == 'object' && el.xtype) {
30387         // created from xtype...
30388         config = el;
30389         ds = el.dataSource;
30390         el = config.container;
30391     }
30392     var items = [];
30393     if (config.items) {
30394         items = config.items;
30395         config.items = [];
30396     }
30397     
30398     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30399     this.ds = ds;
30400     this.cursor = 0;
30401     this.renderButtons(this.el);
30402     this.bind(ds);
30403     
30404     // supprot items array.
30405    
30406     Roo.each(items, function(e) {
30407         this.add(Roo.factory(e));
30408     },this);
30409     
30410 };
30411
30412 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30413     /**
30414      * @cfg {Roo.data.Store} dataSource
30415      * The underlying data store providing the paged data
30416      */
30417     /**
30418      * @cfg {String/HTMLElement/Element} container
30419      * container The id or element that will contain the toolbar
30420      */
30421     /**
30422      * @cfg {Boolean} displayInfo
30423      * True to display the displayMsg (defaults to false)
30424      */
30425     /**
30426      * @cfg {Number} pageSize
30427      * The number of records to display per page (defaults to 20)
30428      */
30429     pageSize: 20,
30430     /**
30431      * @cfg {String} displayMsg
30432      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30433      */
30434     displayMsg : 'Displaying {0} - {1} of {2}',
30435     /**
30436      * @cfg {String} emptyMsg
30437      * The message to display when no records are found (defaults to "No data to display")
30438      */
30439     emptyMsg : 'No data to display',
30440     /**
30441      * Customizable piece of the default paging text (defaults to "Page")
30442      * @type String
30443      */
30444     beforePageText : "Page",
30445     /**
30446      * Customizable piece of the default paging text (defaults to "of %0")
30447      * @type String
30448      */
30449     afterPageText : "of {0}",
30450     /**
30451      * Customizable piece of the default paging text (defaults to "First Page")
30452      * @type String
30453      */
30454     firstText : "First Page",
30455     /**
30456      * Customizable piece of the default paging text (defaults to "Previous Page")
30457      * @type String
30458      */
30459     prevText : "Previous Page",
30460     /**
30461      * Customizable piece of the default paging text (defaults to "Next Page")
30462      * @type String
30463      */
30464     nextText : "Next Page",
30465     /**
30466      * Customizable piece of the default paging text (defaults to "Last Page")
30467      * @type String
30468      */
30469     lastText : "Last Page",
30470     /**
30471      * Customizable piece of the default paging text (defaults to "Refresh")
30472      * @type String
30473      */
30474     refreshText : "Refresh",
30475
30476     // private
30477     renderButtons : function(el){
30478         Roo.PagingToolbar.superclass.render.call(this, el);
30479         this.first = this.addButton({
30480             tooltip: this.firstText,
30481             cls: "x-btn-icon x-grid-page-first",
30482             disabled: true,
30483             handler: this.onClick.createDelegate(this, ["first"])
30484         });
30485         this.prev = this.addButton({
30486             tooltip: this.prevText,
30487             cls: "x-btn-icon x-grid-page-prev",
30488             disabled: true,
30489             handler: this.onClick.createDelegate(this, ["prev"])
30490         });
30491         //this.addSeparator();
30492         this.add(this.beforePageText);
30493         this.field = Roo.get(this.addDom({
30494            tag: "input",
30495            type: "text",
30496            size: "3",
30497            value: "1",
30498            cls: "x-grid-page-number"
30499         }).el);
30500         this.field.on("keydown", this.onPagingKeydown, this);
30501         this.field.on("focus", function(){this.dom.select();});
30502         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30503         this.field.setHeight(18);
30504         //this.addSeparator();
30505         this.next = this.addButton({
30506             tooltip: this.nextText,
30507             cls: "x-btn-icon x-grid-page-next",
30508             disabled: true,
30509             handler: this.onClick.createDelegate(this, ["next"])
30510         });
30511         this.last = this.addButton({
30512             tooltip: this.lastText,
30513             cls: "x-btn-icon x-grid-page-last",
30514             disabled: true,
30515             handler: this.onClick.createDelegate(this, ["last"])
30516         });
30517         //this.addSeparator();
30518         this.loading = this.addButton({
30519             tooltip: this.refreshText,
30520             cls: "x-btn-icon x-grid-loading",
30521             handler: this.onClick.createDelegate(this, ["refresh"])
30522         });
30523
30524         if(this.displayInfo){
30525             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30526         }
30527     },
30528
30529     // private
30530     updateInfo : function(){
30531         if(this.displayEl){
30532             var count = this.ds.getCount();
30533             var msg = count == 0 ?
30534                 this.emptyMsg :
30535                 String.format(
30536                     this.displayMsg,
30537                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30538                 );
30539             this.displayEl.update(msg);
30540         }
30541     },
30542
30543     // private
30544     onLoad : function(ds, r, o){
30545        this.cursor = o.params ? o.params.start : 0;
30546        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30547
30548        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30549        this.field.dom.value = ap;
30550        this.first.setDisabled(ap == 1);
30551        this.prev.setDisabled(ap == 1);
30552        this.next.setDisabled(ap == ps);
30553        this.last.setDisabled(ap == ps);
30554        this.loading.enable();
30555        this.updateInfo();
30556     },
30557
30558     // private
30559     getPageData : function(){
30560         var total = this.ds.getTotalCount();
30561         return {
30562             total : total,
30563             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30564             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30565         };
30566     },
30567
30568     // private
30569     onLoadError : function(){
30570         this.loading.enable();
30571     },
30572
30573     // private
30574     onPagingKeydown : function(e){
30575         var k = e.getKey();
30576         var d = this.getPageData();
30577         if(k == e.RETURN){
30578             var v = this.field.dom.value, pageNum;
30579             if(!v || isNaN(pageNum = parseInt(v, 10))){
30580                 this.field.dom.value = d.activePage;
30581                 return;
30582             }
30583             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30584             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30585             e.stopEvent();
30586         }
30587         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))
30588         {
30589           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30590           this.field.dom.value = pageNum;
30591           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30592           e.stopEvent();
30593         }
30594         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30595         {
30596           var v = this.field.dom.value, pageNum; 
30597           var increment = (e.shiftKey) ? 10 : 1;
30598           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30599             increment *= -1;
30600           }
30601           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30602             this.field.dom.value = d.activePage;
30603             return;
30604           }
30605           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30606           {
30607             this.field.dom.value = parseInt(v, 10) + increment;
30608             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30609             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30610           }
30611           e.stopEvent();
30612         }
30613     },
30614
30615     // private
30616     beforeLoad : function(){
30617         if(this.loading){
30618             this.loading.disable();
30619         }
30620     },
30621
30622     // private
30623     onClick : function(which){
30624         var ds = this.ds;
30625         switch(which){
30626             case "first":
30627                 ds.load({params:{start: 0, limit: this.pageSize}});
30628             break;
30629             case "prev":
30630                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30631             break;
30632             case "next":
30633                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30634             break;
30635             case "last":
30636                 var total = ds.getTotalCount();
30637                 var extra = total % this.pageSize;
30638                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30639                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30640             break;
30641             case "refresh":
30642                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30643             break;
30644         }
30645     },
30646
30647     /**
30648      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30649      * @param {Roo.data.Store} store The data store to unbind
30650      */
30651     unbind : function(ds){
30652         ds.un("beforeload", this.beforeLoad, this);
30653         ds.un("load", this.onLoad, this);
30654         ds.un("loadexception", this.onLoadError, this);
30655         ds.un("remove", this.updateInfo, this);
30656         ds.un("add", this.updateInfo, this);
30657         this.ds = undefined;
30658     },
30659
30660     /**
30661      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30662      * @param {Roo.data.Store} store The data store to bind
30663      */
30664     bind : function(ds){
30665         ds.on("beforeload", this.beforeLoad, this);
30666         ds.on("load", this.onLoad, this);
30667         ds.on("loadexception", this.onLoadError, this);
30668         ds.on("remove", this.updateInfo, this);
30669         ds.on("add", this.updateInfo, this);
30670         this.ds = ds;
30671     }
30672 });/*
30673  * Based on:
30674  * Ext JS Library 1.1.1
30675  * Copyright(c) 2006-2007, Ext JS, LLC.
30676  *
30677  * Originally Released Under LGPL - original licence link has changed is not relivant.
30678  *
30679  * Fork - LGPL
30680  * <script type="text/javascript">
30681  */
30682
30683 /**
30684  * @class Roo.Resizable
30685  * @extends Roo.util.Observable
30686  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30687  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30688  * 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
30689  * the element will be wrapped for you automatically.</p>
30690  * <p>Here is the list of valid resize handles:</p>
30691  * <pre>
30692 Value   Description
30693 ------  -------------------
30694  'n'     north
30695  's'     south
30696  'e'     east
30697  'w'     west
30698  'nw'    northwest
30699  'sw'    southwest
30700  'se'    southeast
30701  'ne'    northeast
30702  'hd'    horizontal drag
30703  'all'   all
30704 </pre>
30705  * <p>Here's an example showing the creation of a typical Resizable:</p>
30706  * <pre><code>
30707 var resizer = new Roo.Resizable("element-id", {
30708     handles: 'all',
30709     minWidth: 200,
30710     minHeight: 100,
30711     maxWidth: 500,
30712     maxHeight: 400,
30713     pinned: true
30714 });
30715 resizer.on("resize", myHandler);
30716 </code></pre>
30717  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30718  * resizer.east.setDisplayed(false);</p>
30719  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30720  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30721  * resize operation's new size (defaults to [0, 0])
30722  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30723  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30724  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30725  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30726  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30727  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30728  * @cfg {Number} width The width of the element in pixels (defaults to null)
30729  * @cfg {Number} height The height of the element in pixels (defaults to null)
30730  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30731  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30732  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30733  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30734  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30735  * in favor of the handles config option (defaults to false)
30736  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30737  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30738  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30739  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30740  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30741  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30742  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30743  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30744  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30745  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30746  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30747  * @constructor
30748  * Create a new resizable component
30749  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30750  * @param {Object} config configuration options
30751   */
30752 Roo.Resizable = function(el, config)
30753 {
30754     this.el = Roo.get(el);
30755
30756     if(config && config.wrap){
30757         config.resizeChild = this.el;
30758         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30759         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30760         this.el.setStyle("overflow", "hidden");
30761         this.el.setPositioning(config.resizeChild.getPositioning());
30762         config.resizeChild.clearPositioning();
30763         if(!config.width || !config.height){
30764             var csize = config.resizeChild.getSize();
30765             this.el.setSize(csize.width, csize.height);
30766         }
30767         if(config.pinned && !config.adjustments){
30768             config.adjustments = "auto";
30769         }
30770     }
30771
30772     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30773     this.proxy.unselectable();
30774     this.proxy.enableDisplayMode('block');
30775
30776     Roo.apply(this, config);
30777
30778     if(this.pinned){
30779         this.disableTrackOver = true;
30780         this.el.addClass("x-resizable-pinned");
30781     }
30782     // if the element isn't positioned, make it relative
30783     var position = this.el.getStyle("position");
30784     if(position != "absolute" && position != "fixed"){
30785         this.el.setStyle("position", "relative");
30786     }
30787     if(!this.handles){ // no handles passed, must be legacy style
30788         this.handles = 's,e,se';
30789         if(this.multiDirectional){
30790             this.handles += ',n,w';
30791         }
30792     }
30793     if(this.handles == "all"){
30794         this.handles = "n s e w ne nw se sw";
30795     }
30796     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30797     var ps = Roo.Resizable.positions;
30798     for(var i = 0, len = hs.length; i < len; i++){
30799         if(hs[i] && ps[hs[i]]){
30800             var pos = ps[hs[i]];
30801             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30802         }
30803     }
30804     // legacy
30805     this.corner = this.southeast;
30806     
30807     // updateBox = the box can move..
30808     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30809         this.updateBox = true;
30810     }
30811
30812     this.activeHandle = null;
30813
30814     if(this.resizeChild){
30815         if(typeof this.resizeChild == "boolean"){
30816             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30817         }else{
30818             this.resizeChild = Roo.get(this.resizeChild, true);
30819         }
30820     }
30821     
30822     if(this.adjustments == "auto"){
30823         var rc = this.resizeChild;
30824         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30825         if(rc && (hw || hn)){
30826             rc.position("relative");
30827             rc.setLeft(hw ? hw.el.getWidth() : 0);
30828             rc.setTop(hn ? hn.el.getHeight() : 0);
30829         }
30830         this.adjustments = [
30831             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30832             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30833         ];
30834     }
30835
30836     if(this.draggable){
30837         this.dd = this.dynamic ?
30838             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30839         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30840     }
30841
30842     // public events
30843     this.addEvents({
30844         /**
30845          * @event beforeresize
30846          * Fired before resize is allowed. Set enabled to false to cancel resize.
30847          * @param {Roo.Resizable} this
30848          * @param {Roo.EventObject} e The mousedown event
30849          */
30850         "beforeresize" : true,
30851         /**
30852          * @event resizing
30853          * Fired a resizing.
30854          * @param {Roo.Resizable} this
30855          * @param {Number} x The new x position
30856          * @param {Number} y The new y position
30857          * @param {Number} w The new w width
30858          * @param {Number} h The new h hight
30859          * @param {Roo.EventObject} e The mouseup event
30860          */
30861         "resizing" : true,
30862         /**
30863          * @event resize
30864          * Fired after a resize.
30865          * @param {Roo.Resizable} this
30866          * @param {Number} width The new width
30867          * @param {Number} height The new height
30868          * @param {Roo.EventObject} e The mouseup event
30869          */
30870         "resize" : true
30871     });
30872
30873     if(this.width !== null && this.height !== null){
30874         this.resizeTo(this.width, this.height);
30875     }else{
30876         this.updateChildSize();
30877     }
30878     if(Roo.isIE){
30879         this.el.dom.style.zoom = 1;
30880     }
30881     Roo.Resizable.superclass.constructor.call(this);
30882 };
30883
30884 Roo.extend(Roo.Resizable, Roo.util.Observable, {
30885         resizeChild : false,
30886         adjustments : [0, 0],
30887         minWidth : 5,
30888         minHeight : 5,
30889         maxWidth : 10000,
30890         maxHeight : 10000,
30891         enabled : true,
30892         animate : false,
30893         duration : .35,
30894         dynamic : false,
30895         handles : false,
30896         multiDirectional : false,
30897         disableTrackOver : false,
30898         easing : 'easeOutStrong',
30899         widthIncrement : 0,
30900         heightIncrement : 0,
30901         pinned : false,
30902         width : null,
30903         height : null,
30904         preserveRatio : false,
30905         transparent: false,
30906         minX: 0,
30907         minY: 0,
30908         draggable: false,
30909
30910         /**
30911          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
30912          */
30913         constrainTo: undefined,
30914         /**
30915          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
30916          */
30917         resizeRegion: undefined,
30918
30919
30920     /**
30921      * Perform a manual resize
30922      * @param {Number} width
30923      * @param {Number} height
30924      */
30925     resizeTo : function(width, height){
30926         this.el.setSize(width, height);
30927         this.updateChildSize();
30928         this.fireEvent("resize", this, width, height, null);
30929     },
30930
30931     // private
30932     startSizing : function(e, handle){
30933         this.fireEvent("beforeresize", this, e);
30934         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
30935
30936             if(!this.overlay){
30937                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
30938                 this.overlay.unselectable();
30939                 this.overlay.enableDisplayMode("block");
30940                 this.overlay.on("mousemove", this.onMouseMove, this);
30941                 this.overlay.on("mouseup", this.onMouseUp, this);
30942             }
30943             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
30944
30945             this.resizing = true;
30946             this.startBox = this.el.getBox();
30947             this.startPoint = e.getXY();
30948             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
30949                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
30950
30951             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30952             this.overlay.show();
30953
30954             if(this.constrainTo) {
30955                 var ct = Roo.get(this.constrainTo);
30956                 this.resizeRegion = ct.getRegion().adjust(
30957                     ct.getFrameWidth('t'),
30958                     ct.getFrameWidth('l'),
30959                     -ct.getFrameWidth('b'),
30960                     -ct.getFrameWidth('r')
30961                 );
30962             }
30963
30964             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
30965             this.proxy.show();
30966             this.proxy.setBox(this.startBox);
30967             if(!this.dynamic){
30968                 this.proxy.setStyle('visibility', 'visible');
30969             }
30970         }
30971     },
30972
30973     // private
30974     onMouseDown : function(handle, e){
30975         if(this.enabled){
30976             e.stopEvent();
30977             this.activeHandle = handle;
30978             this.startSizing(e, handle);
30979         }
30980     },
30981
30982     // private
30983     onMouseUp : function(e){
30984         var size = this.resizeElement();
30985         this.resizing = false;
30986         this.handleOut();
30987         this.overlay.hide();
30988         this.proxy.hide();
30989         this.fireEvent("resize", this, size.width, size.height, e);
30990     },
30991
30992     // private
30993     updateChildSize : function(){
30994         
30995         if(this.resizeChild){
30996             var el = this.el;
30997             var child = this.resizeChild;
30998             var adj = this.adjustments;
30999             if(el.dom.offsetWidth){
31000                 var b = el.getSize(true);
31001                 child.setSize(b.width+adj[0], b.height+adj[1]);
31002             }
31003             // Second call here for IE
31004             // The first call enables instant resizing and
31005             // the second call corrects scroll bars if they
31006             // exist
31007             if(Roo.isIE){
31008                 setTimeout(function(){
31009                     if(el.dom.offsetWidth){
31010                         var b = el.getSize(true);
31011                         child.setSize(b.width+adj[0], b.height+adj[1]);
31012                     }
31013                 }, 10);
31014             }
31015         }
31016     },
31017
31018     // private
31019     snap : function(value, inc, min){
31020         if(!inc || !value) {
31021             return value;
31022         }
31023         var newValue = value;
31024         var m = value % inc;
31025         if(m > 0){
31026             if(m > (inc/2)){
31027                 newValue = value + (inc-m);
31028             }else{
31029                 newValue = value - m;
31030             }
31031         }
31032         return Math.max(min, newValue);
31033     },
31034
31035     // private
31036     resizeElement : function(){
31037         var box = this.proxy.getBox();
31038         if(this.updateBox){
31039             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31040         }else{
31041             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31042         }
31043         this.updateChildSize();
31044         if(!this.dynamic){
31045             this.proxy.hide();
31046         }
31047         return box;
31048     },
31049
31050     // private
31051     constrain : function(v, diff, m, mx){
31052         if(v - diff < m){
31053             diff = v - m;
31054         }else if(v - diff > mx){
31055             diff = mx - v;
31056         }
31057         return diff;
31058     },
31059
31060     // private
31061     onMouseMove : function(e){
31062         
31063         if(this.enabled){
31064             try{// try catch so if something goes wrong the user doesn't get hung
31065
31066             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31067                 return;
31068             }
31069
31070             //var curXY = this.startPoint;
31071             var curSize = this.curSize || this.startBox;
31072             var x = this.startBox.x, y = this.startBox.y;
31073             var ox = x, oy = y;
31074             var w = curSize.width, h = curSize.height;
31075             var ow = w, oh = h;
31076             var mw = this.minWidth, mh = this.minHeight;
31077             var mxw = this.maxWidth, mxh = this.maxHeight;
31078             var wi = this.widthIncrement;
31079             var hi = this.heightIncrement;
31080
31081             var eventXY = e.getXY();
31082             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31083             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31084
31085             var pos = this.activeHandle.position;
31086
31087             switch(pos){
31088                 case "east":
31089                     w += diffX;
31090                     w = Math.min(Math.max(mw, w), mxw);
31091                     break;
31092              
31093                 case "south":
31094                     h += diffY;
31095                     h = Math.min(Math.max(mh, h), mxh);
31096                     break;
31097                 case "southeast":
31098                     w += diffX;
31099                     h += diffY;
31100                     w = Math.min(Math.max(mw, w), mxw);
31101                     h = Math.min(Math.max(mh, h), mxh);
31102                     break;
31103                 case "north":
31104                     diffY = this.constrain(h, diffY, mh, mxh);
31105                     y += diffY;
31106                     h -= diffY;
31107                     break;
31108                 case "hdrag":
31109                     
31110                     if (wi) {
31111                         var adiffX = Math.abs(diffX);
31112                         var sub = (adiffX % wi); // how much 
31113                         if (sub > (wi/2)) { // far enough to snap
31114                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31115                         } else {
31116                             // remove difference.. 
31117                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31118                         }
31119                     }
31120                     x += diffX;
31121                     x = Math.max(this.minX, x);
31122                     break;
31123                 case "west":
31124                     diffX = this.constrain(w, diffX, mw, mxw);
31125                     x += diffX;
31126                     w -= diffX;
31127                     break;
31128                 case "northeast":
31129                     w += diffX;
31130                     w = Math.min(Math.max(mw, w), mxw);
31131                     diffY = this.constrain(h, diffY, mh, mxh);
31132                     y += diffY;
31133                     h -= diffY;
31134                     break;
31135                 case "northwest":
31136                     diffX = this.constrain(w, diffX, mw, mxw);
31137                     diffY = this.constrain(h, diffY, mh, mxh);
31138                     y += diffY;
31139                     h -= diffY;
31140                     x += diffX;
31141                     w -= diffX;
31142                     break;
31143                case "southwest":
31144                     diffX = this.constrain(w, diffX, mw, mxw);
31145                     h += diffY;
31146                     h = Math.min(Math.max(mh, h), mxh);
31147                     x += diffX;
31148                     w -= diffX;
31149                     break;
31150             }
31151
31152             var sw = this.snap(w, wi, mw);
31153             var sh = this.snap(h, hi, mh);
31154             if(sw != w || sh != h){
31155                 switch(pos){
31156                     case "northeast":
31157                         y -= sh - h;
31158                     break;
31159                     case "north":
31160                         y -= sh - h;
31161                         break;
31162                     case "southwest":
31163                         x -= sw - w;
31164                     break;
31165                     case "west":
31166                         x -= sw - w;
31167                         break;
31168                     case "northwest":
31169                         x -= sw - w;
31170                         y -= sh - h;
31171                     break;
31172                 }
31173                 w = sw;
31174                 h = sh;
31175             }
31176
31177             if(this.preserveRatio){
31178                 switch(pos){
31179                     case "southeast":
31180                     case "east":
31181                         h = oh * (w/ow);
31182                         h = Math.min(Math.max(mh, h), mxh);
31183                         w = ow * (h/oh);
31184                        break;
31185                     case "south":
31186                         w = ow * (h/oh);
31187                         w = Math.min(Math.max(mw, w), mxw);
31188                         h = oh * (w/ow);
31189                         break;
31190                     case "northeast":
31191                         w = ow * (h/oh);
31192                         w = Math.min(Math.max(mw, w), mxw);
31193                         h = oh * (w/ow);
31194                     break;
31195                     case "north":
31196                         var tw = w;
31197                         w = ow * (h/oh);
31198                         w = Math.min(Math.max(mw, w), mxw);
31199                         h = oh * (w/ow);
31200                         x += (tw - w) / 2;
31201                         break;
31202                     case "southwest":
31203                         h = oh * (w/ow);
31204                         h = Math.min(Math.max(mh, h), mxh);
31205                         var tw = w;
31206                         w = ow * (h/oh);
31207                         x += tw - w;
31208                         break;
31209                     case "west":
31210                         var th = h;
31211                         h = oh * (w/ow);
31212                         h = Math.min(Math.max(mh, h), mxh);
31213                         y += (th - h) / 2;
31214                         var tw = w;
31215                         w = ow * (h/oh);
31216                         x += tw - w;
31217                        break;
31218                     case "northwest":
31219                         var tw = w;
31220                         var th = h;
31221                         h = oh * (w/ow);
31222                         h = Math.min(Math.max(mh, h), mxh);
31223                         w = ow * (h/oh);
31224                         y += th - h;
31225                         x += tw - w;
31226                        break;
31227
31228                 }
31229             }
31230             if (pos == 'hdrag') {
31231                 w = ow;
31232             }
31233             this.proxy.setBounds(x, y, w, h);
31234             if(this.dynamic){
31235                 this.resizeElement();
31236             }
31237             }catch(e){}
31238         }
31239         this.fireEvent("resizing", this, x, y, w, h, e);
31240     },
31241
31242     // private
31243     handleOver : function(){
31244         if(this.enabled){
31245             this.el.addClass("x-resizable-over");
31246         }
31247     },
31248
31249     // private
31250     handleOut : function(){
31251         if(!this.resizing){
31252             this.el.removeClass("x-resizable-over");
31253         }
31254     },
31255
31256     /**
31257      * Returns the element this component is bound to.
31258      * @return {Roo.Element}
31259      */
31260     getEl : function(){
31261         return this.el;
31262     },
31263
31264     /**
31265      * Returns the resizeChild element (or null).
31266      * @return {Roo.Element}
31267      */
31268     getResizeChild : function(){
31269         return this.resizeChild;
31270     },
31271     groupHandler : function()
31272     {
31273         
31274     },
31275     /**
31276      * Destroys this resizable. If the element was wrapped and
31277      * removeEl is not true then the element remains.
31278      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31279      */
31280     destroy : function(removeEl){
31281         this.proxy.remove();
31282         if(this.overlay){
31283             this.overlay.removeAllListeners();
31284             this.overlay.remove();
31285         }
31286         var ps = Roo.Resizable.positions;
31287         for(var k in ps){
31288             if(typeof ps[k] != "function" && this[ps[k]]){
31289                 var h = this[ps[k]];
31290                 h.el.removeAllListeners();
31291                 h.el.remove();
31292             }
31293         }
31294         if(removeEl){
31295             this.el.update("");
31296             this.el.remove();
31297         }
31298     }
31299 });
31300
31301 // private
31302 // hash to map config positions to true positions
31303 Roo.Resizable.positions = {
31304     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31305     hd: "hdrag"
31306 };
31307
31308 // private
31309 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31310     if(!this.tpl){
31311         // only initialize the template if resizable is used
31312         var tpl = Roo.DomHelper.createTemplate(
31313             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31314         );
31315         tpl.compile();
31316         Roo.Resizable.Handle.prototype.tpl = tpl;
31317     }
31318     this.position = pos;
31319     this.rz = rz;
31320     // show north drag fro topdra
31321     var handlepos = pos == 'hdrag' ? 'north' : pos;
31322     
31323     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31324     if (pos == 'hdrag') {
31325         this.el.setStyle('cursor', 'pointer');
31326     }
31327     this.el.unselectable();
31328     if(transparent){
31329         this.el.setOpacity(0);
31330     }
31331     this.el.on("mousedown", this.onMouseDown, this);
31332     if(!disableTrackOver){
31333         this.el.on("mouseover", this.onMouseOver, this);
31334         this.el.on("mouseout", this.onMouseOut, this);
31335     }
31336 };
31337
31338 // private
31339 Roo.Resizable.Handle.prototype = {
31340     afterResize : function(rz){
31341         Roo.log('after?');
31342         // do nothing
31343     },
31344     // private
31345     onMouseDown : function(e){
31346         this.rz.onMouseDown(this, e);
31347     },
31348     // private
31349     onMouseOver : function(e){
31350         this.rz.handleOver(this, e);
31351     },
31352     // private
31353     onMouseOut : function(e){
31354         this.rz.handleOut(this, e);
31355     }
31356 };/*
31357  * Based on:
31358  * Ext JS Library 1.1.1
31359  * Copyright(c) 2006-2007, Ext JS, LLC.
31360  *
31361  * Originally Released Under LGPL - original licence link has changed is not relivant.
31362  *
31363  * Fork - LGPL
31364  * <script type="text/javascript">
31365  */
31366
31367 /**
31368  * @class Roo.Editor
31369  * @extends Roo.Component
31370  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31371  * @constructor
31372  * Create a new Editor
31373  * @param {Roo.form.Field} field The Field object (or descendant)
31374  * @param {Object} config The config object
31375  */
31376 Roo.Editor = function(field, config){
31377     Roo.Editor.superclass.constructor.call(this, config);
31378     this.field = field;
31379     this.addEvents({
31380         /**
31381              * @event beforestartedit
31382              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31383              * false from the handler of this event.
31384              * @param {Editor} this
31385              * @param {Roo.Element} boundEl The underlying element bound to this editor
31386              * @param {Mixed} value The field value being set
31387              */
31388         "beforestartedit" : true,
31389         /**
31390              * @event startedit
31391              * Fires when this editor is displayed
31392              * @param {Roo.Element} boundEl The underlying element bound to this editor
31393              * @param {Mixed} value The starting field value
31394              */
31395         "startedit" : true,
31396         /**
31397              * @event beforecomplete
31398              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31399              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31400              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31401              * event will not fire since no edit actually occurred.
31402              * @param {Editor} this
31403              * @param {Mixed} value The current field value
31404              * @param {Mixed} startValue The original field value
31405              */
31406         "beforecomplete" : true,
31407         /**
31408              * @event complete
31409              * Fires after editing is complete and any changed value has been written to the underlying field.
31410              * @param {Editor} this
31411              * @param {Mixed} value The current field value
31412              * @param {Mixed} startValue The original field value
31413              */
31414         "complete" : true,
31415         /**
31416          * @event specialkey
31417          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31418          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31419          * @param {Roo.form.Field} this
31420          * @param {Roo.EventObject} e The event object
31421          */
31422         "specialkey" : true
31423     });
31424 };
31425
31426 Roo.extend(Roo.Editor, Roo.Component, {
31427     /**
31428      * @cfg {Boolean/String} autosize
31429      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31430      * or "height" to adopt the height only (defaults to false)
31431      */
31432     /**
31433      * @cfg {Boolean} revertInvalid
31434      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31435      * validation fails (defaults to true)
31436      */
31437     /**
31438      * @cfg {Boolean} ignoreNoChange
31439      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31440      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31441      * will never be ignored.
31442      */
31443     /**
31444      * @cfg {Boolean} hideEl
31445      * False to keep the bound element visible while the editor is displayed (defaults to true)
31446      */
31447     /**
31448      * @cfg {Mixed} value
31449      * The data value of the underlying field (defaults to "")
31450      */
31451     value : "",
31452     /**
31453      * @cfg {String} alignment
31454      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31455      */
31456     alignment: "c-c?",
31457     /**
31458      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31459      * for bottom-right shadow (defaults to "frame")
31460      */
31461     shadow : "frame",
31462     /**
31463      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31464      */
31465     constrain : false,
31466     /**
31467      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31468      */
31469     completeOnEnter : false,
31470     /**
31471      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31472      */
31473     cancelOnEsc : false,
31474     /**
31475      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31476      */
31477     updateEl : false,
31478
31479     // private
31480     onRender : function(ct, position){
31481         this.el = new Roo.Layer({
31482             shadow: this.shadow,
31483             cls: "x-editor",
31484             parentEl : ct,
31485             shim : this.shim,
31486             shadowOffset:4,
31487             id: this.id,
31488             constrain: this.constrain
31489         });
31490         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31491         if(this.field.msgTarget != 'title'){
31492             this.field.msgTarget = 'qtip';
31493         }
31494         this.field.render(this.el);
31495         if(Roo.isGecko){
31496             this.field.el.dom.setAttribute('autocomplete', 'off');
31497         }
31498         this.field.on("specialkey", this.onSpecialKey, this);
31499         if(this.swallowKeys){
31500             this.field.el.swallowEvent(['keydown','keypress']);
31501         }
31502         this.field.show();
31503         this.field.on("blur", this.onBlur, this);
31504         if(this.field.grow){
31505             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31506         }
31507     },
31508
31509     onSpecialKey : function(field, e)
31510     {
31511         //Roo.log('editor onSpecialKey');
31512         if(this.completeOnEnter && e.getKey() == e.ENTER){
31513             e.stopEvent();
31514             this.completeEdit();
31515             return;
31516         }
31517         // do not fire special key otherwise it might hide close the editor...
31518         if(e.getKey() == e.ENTER){    
31519             return;
31520         }
31521         if(this.cancelOnEsc && e.getKey() == e.ESC){
31522             this.cancelEdit();
31523             return;
31524         } 
31525         this.fireEvent('specialkey', field, e);
31526     
31527     },
31528
31529     /**
31530      * Starts the editing process and shows the editor.
31531      * @param {String/HTMLElement/Element} el The element to edit
31532      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31533       * to the innerHTML of el.
31534      */
31535     startEdit : function(el, value){
31536         if(this.editing){
31537             this.completeEdit();
31538         }
31539         this.boundEl = Roo.get(el);
31540         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31541         if(!this.rendered){
31542             this.render(this.parentEl || document.body);
31543         }
31544         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31545             return;
31546         }
31547         this.startValue = v;
31548         this.field.setValue(v);
31549         if(this.autoSize){
31550             var sz = this.boundEl.getSize();
31551             switch(this.autoSize){
31552                 case "width":
31553                 this.setSize(sz.width,  "");
31554                 break;
31555                 case "height":
31556                 this.setSize("",  sz.height);
31557                 break;
31558                 default:
31559                 this.setSize(sz.width,  sz.height);
31560             }
31561         }
31562         this.el.alignTo(this.boundEl, this.alignment);
31563         this.editing = true;
31564         if(Roo.QuickTips){
31565             Roo.QuickTips.disable();
31566         }
31567         this.show();
31568     },
31569
31570     /**
31571      * Sets the height and width of this editor.
31572      * @param {Number} width The new width
31573      * @param {Number} height The new height
31574      */
31575     setSize : function(w, h){
31576         this.field.setSize(w, h);
31577         if(this.el){
31578             this.el.sync();
31579         }
31580     },
31581
31582     /**
31583      * Realigns the editor to the bound field based on the current alignment config value.
31584      */
31585     realign : function(){
31586         this.el.alignTo(this.boundEl, this.alignment);
31587     },
31588
31589     /**
31590      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31591      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31592      */
31593     completeEdit : function(remainVisible){
31594         if(!this.editing){
31595             return;
31596         }
31597         var v = this.getValue();
31598         if(this.revertInvalid !== false && !this.field.isValid()){
31599             v = this.startValue;
31600             this.cancelEdit(true);
31601         }
31602         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31603             this.editing = false;
31604             this.hide();
31605             return;
31606         }
31607         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31608             this.editing = false;
31609             if(this.updateEl && this.boundEl){
31610                 this.boundEl.update(v);
31611             }
31612             if(remainVisible !== true){
31613                 this.hide();
31614             }
31615             this.fireEvent("complete", this, v, this.startValue);
31616         }
31617     },
31618
31619     // private
31620     onShow : function(){
31621         this.el.show();
31622         if(this.hideEl !== false){
31623             this.boundEl.hide();
31624         }
31625         this.field.show();
31626         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31627             this.fixIEFocus = true;
31628             this.deferredFocus.defer(50, this);
31629         }else{
31630             this.field.focus();
31631         }
31632         this.fireEvent("startedit", this.boundEl, this.startValue);
31633     },
31634
31635     deferredFocus : function(){
31636         if(this.editing){
31637             this.field.focus();
31638         }
31639     },
31640
31641     /**
31642      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31643      * reverted to the original starting value.
31644      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31645      * cancel (defaults to false)
31646      */
31647     cancelEdit : function(remainVisible){
31648         if(this.editing){
31649             this.setValue(this.startValue);
31650             if(remainVisible !== true){
31651                 this.hide();
31652             }
31653         }
31654     },
31655
31656     // private
31657     onBlur : function(){
31658         if(this.allowBlur !== true && this.editing){
31659             this.completeEdit();
31660         }
31661     },
31662
31663     // private
31664     onHide : function(){
31665         if(this.editing){
31666             this.completeEdit();
31667             return;
31668         }
31669         this.field.blur();
31670         if(this.field.collapse){
31671             this.field.collapse();
31672         }
31673         this.el.hide();
31674         if(this.hideEl !== false){
31675             this.boundEl.show();
31676         }
31677         if(Roo.QuickTips){
31678             Roo.QuickTips.enable();
31679         }
31680     },
31681
31682     /**
31683      * Sets the data value of the editor
31684      * @param {Mixed} value Any valid value supported by the underlying field
31685      */
31686     setValue : function(v){
31687         this.field.setValue(v);
31688     },
31689
31690     /**
31691      * Gets the data value of the editor
31692      * @return {Mixed} The data value
31693      */
31694     getValue : function(){
31695         return this.field.getValue();
31696     }
31697 });/*
31698  * Based on:
31699  * Ext JS Library 1.1.1
31700  * Copyright(c) 2006-2007, Ext JS, LLC.
31701  *
31702  * Originally Released Under LGPL - original licence link has changed is not relivant.
31703  *
31704  * Fork - LGPL
31705  * <script type="text/javascript">
31706  */
31707  
31708 /**
31709  * @class Roo.BasicDialog
31710  * @extends Roo.util.Observable
31711  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31712  * <pre><code>
31713 var dlg = new Roo.BasicDialog("my-dlg", {
31714     height: 200,
31715     width: 300,
31716     minHeight: 100,
31717     minWidth: 150,
31718     modal: true,
31719     proxyDrag: true,
31720     shadow: true
31721 });
31722 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31723 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31724 dlg.addButton('Cancel', dlg.hide, dlg);
31725 dlg.show();
31726 </code></pre>
31727   <b>A Dialog should always be a direct child of the body element.</b>
31728  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31729  * @cfg {String} title Default text to display in the title bar (defaults to null)
31730  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31731  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31732  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31733  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31734  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31735  * (defaults to null with no animation)
31736  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31737  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31738  * property for valid values (defaults to 'all')
31739  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31740  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31741  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31742  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31743  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31744  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31745  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31746  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31747  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31748  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31749  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31750  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31751  * draggable = true (defaults to false)
31752  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31753  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31754  * shadow (defaults to false)
31755  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31756  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31757  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31758  * @cfg {Array} buttons Array of buttons
31759  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31760  * @constructor
31761  * Create a new BasicDialog.
31762  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31763  * @param {Object} config Configuration options
31764  */
31765 Roo.BasicDialog = function(el, config){
31766     this.el = Roo.get(el);
31767     var dh = Roo.DomHelper;
31768     if(!this.el && config && config.autoCreate){
31769         if(typeof config.autoCreate == "object"){
31770             if(!config.autoCreate.id){
31771                 config.autoCreate.id = el;
31772             }
31773             this.el = dh.append(document.body,
31774                         config.autoCreate, true);
31775         }else{
31776             this.el = dh.append(document.body,
31777                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31778         }
31779     }
31780     el = this.el;
31781     el.setDisplayed(true);
31782     el.hide = this.hideAction;
31783     this.id = el.id;
31784     el.addClass("x-dlg");
31785
31786     Roo.apply(this, config);
31787
31788     this.proxy = el.createProxy("x-dlg-proxy");
31789     this.proxy.hide = this.hideAction;
31790     this.proxy.setOpacity(.5);
31791     this.proxy.hide();
31792
31793     if(config.width){
31794         el.setWidth(config.width);
31795     }
31796     if(config.height){
31797         el.setHeight(config.height);
31798     }
31799     this.size = el.getSize();
31800     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31801         this.xy = [config.x,config.y];
31802     }else{
31803         this.xy = el.getCenterXY(true);
31804     }
31805     /** The header element @type Roo.Element */
31806     this.header = el.child("> .x-dlg-hd");
31807     /** The body element @type Roo.Element */
31808     this.body = el.child("> .x-dlg-bd");
31809     /** The footer element @type Roo.Element */
31810     this.footer = el.child("> .x-dlg-ft");
31811
31812     if(!this.header){
31813         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31814     }
31815     if(!this.body){
31816         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31817     }
31818
31819     this.header.unselectable();
31820     if(this.title){
31821         this.header.update(this.title);
31822     }
31823     // this element allows the dialog to be focused for keyboard event
31824     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31825     this.focusEl.swallowEvent("click", true);
31826
31827     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31828
31829     // wrap the body and footer for special rendering
31830     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31831     if(this.footer){
31832         this.bwrap.dom.appendChild(this.footer.dom);
31833     }
31834
31835     this.bg = this.el.createChild({
31836         tag: "div", cls:"x-dlg-bg",
31837         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31838     });
31839     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31840
31841
31842     if(this.autoScroll !== false && !this.autoTabs){
31843         this.body.setStyle("overflow", "auto");
31844     }
31845
31846     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
31847
31848     if(this.closable !== false){
31849         this.el.addClass("x-dlg-closable");
31850         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
31851         this.close.on("click", this.closeClick, this);
31852         this.close.addClassOnOver("x-dlg-close-over");
31853     }
31854     if(this.collapsible !== false){
31855         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
31856         this.collapseBtn.on("click", this.collapseClick, this);
31857         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
31858         this.header.on("dblclick", this.collapseClick, this);
31859     }
31860     if(this.resizable !== false){
31861         this.el.addClass("x-dlg-resizable");
31862         this.resizer = new Roo.Resizable(el, {
31863             minWidth: this.minWidth || 80,
31864             minHeight:this.minHeight || 80,
31865             handles: this.resizeHandles || "all",
31866             pinned: true
31867         });
31868         this.resizer.on("beforeresize", this.beforeResize, this);
31869         this.resizer.on("resize", this.onResize, this);
31870     }
31871     if(this.draggable !== false){
31872         el.addClass("x-dlg-draggable");
31873         if (!this.proxyDrag) {
31874             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
31875         }
31876         else {
31877             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
31878         }
31879         dd.setHandleElId(this.header.id);
31880         dd.endDrag = this.endMove.createDelegate(this);
31881         dd.startDrag = this.startMove.createDelegate(this);
31882         dd.onDrag = this.onDrag.createDelegate(this);
31883         dd.scroll = false;
31884         this.dd = dd;
31885     }
31886     if(this.modal){
31887         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
31888         this.mask.enableDisplayMode("block");
31889         this.mask.hide();
31890         this.el.addClass("x-dlg-modal");
31891     }
31892     if(this.shadow){
31893         this.shadow = new Roo.Shadow({
31894             mode : typeof this.shadow == "string" ? this.shadow : "sides",
31895             offset : this.shadowOffset
31896         });
31897     }else{
31898         this.shadowOffset = 0;
31899     }
31900     if(Roo.useShims && this.shim !== false){
31901         this.shim = this.el.createShim();
31902         this.shim.hide = this.hideAction;
31903         this.shim.hide();
31904     }else{
31905         this.shim = false;
31906     }
31907     if(this.autoTabs){
31908         this.initTabs();
31909     }
31910     if (this.buttons) { 
31911         var bts= this.buttons;
31912         this.buttons = [];
31913         Roo.each(bts, function(b) {
31914             this.addButton(b);
31915         }, this);
31916     }
31917     
31918     
31919     this.addEvents({
31920         /**
31921          * @event keydown
31922          * Fires when a key is pressed
31923          * @param {Roo.BasicDialog} this
31924          * @param {Roo.EventObject} e
31925          */
31926         "keydown" : true,
31927         /**
31928          * @event move
31929          * Fires when this dialog is moved by the user.
31930          * @param {Roo.BasicDialog} this
31931          * @param {Number} x The new page X
31932          * @param {Number} y The new page Y
31933          */
31934         "move" : true,
31935         /**
31936          * @event resize
31937          * Fires when this dialog is resized by the user.
31938          * @param {Roo.BasicDialog} this
31939          * @param {Number} width The new width
31940          * @param {Number} height The new height
31941          */
31942         "resize" : true,
31943         /**
31944          * @event beforehide
31945          * Fires before this dialog is hidden.
31946          * @param {Roo.BasicDialog} this
31947          */
31948         "beforehide" : true,
31949         /**
31950          * @event hide
31951          * Fires when this dialog is hidden.
31952          * @param {Roo.BasicDialog} this
31953          */
31954         "hide" : true,
31955         /**
31956          * @event beforeshow
31957          * Fires before this dialog is shown.
31958          * @param {Roo.BasicDialog} this
31959          */
31960         "beforeshow" : true,
31961         /**
31962          * @event show
31963          * Fires when this dialog is shown.
31964          * @param {Roo.BasicDialog} this
31965          */
31966         "show" : true
31967     });
31968     el.on("keydown", this.onKeyDown, this);
31969     el.on("mousedown", this.toFront, this);
31970     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
31971     this.el.hide();
31972     Roo.DialogManager.register(this);
31973     Roo.BasicDialog.superclass.constructor.call(this);
31974 };
31975
31976 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
31977     shadowOffset: Roo.isIE ? 6 : 5,
31978     minHeight: 80,
31979     minWidth: 200,
31980     minButtonWidth: 75,
31981     defaultButton: null,
31982     buttonAlign: "right",
31983     tabTag: 'div',
31984     firstShow: true,
31985
31986     /**
31987      * Sets the dialog title text
31988      * @param {String} text The title text to display
31989      * @return {Roo.BasicDialog} this
31990      */
31991     setTitle : function(text){
31992         this.header.update(text);
31993         return this;
31994     },
31995
31996     // private
31997     closeClick : function(){
31998         this.hide();
31999     },
32000
32001     // private
32002     collapseClick : function(){
32003         this[this.collapsed ? "expand" : "collapse"]();
32004     },
32005
32006     /**
32007      * Collapses the dialog to its minimized state (only the title bar is visible).
32008      * Equivalent to the user clicking the collapse dialog button.
32009      */
32010     collapse : function(){
32011         if(!this.collapsed){
32012             this.collapsed = true;
32013             this.el.addClass("x-dlg-collapsed");
32014             this.restoreHeight = this.el.getHeight();
32015             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32016         }
32017     },
32018
32019     /**
32020      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32021      * clicking the expand dialog button.
32022      */
32023     expand : function(){
32024         if(this.collapsed){
32025             this.collapsed = false;
32026             this.el.removeClass("x-dlg-collapsed");
32027             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32028         }
32029     },
32030
32031     /**
32032      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32033      * @return {Roo.TabPanel} The tabs component
32034      */
32035     initTabs : function(){
32036         var tabs = this.getTabs();
32037         while(tabs.getTab(0)){
32038             tabs.removeTab(0);
32039         }
32040         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32041             var dom = el.dom;
32042             tabs.addTab(Roo.id(dom), dom.title);
32043             dom.title = "";
32044         });
32045         tabs.activate(0);
32046         return tabs;
32047     },
32048
32049     // private
32050     beforeResize : function(){
32051         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32052     },
32053
32054     // private
32055     onResize : function(){
32056         this.refreshSize();
32057         this.syncBodyHeight();
32058         this.adjustAssets();
32059         this.focus();
32060         this.fireEvent("resize", this, this.size.width, this.size.height);
32061     },
32062
32063     // private
32064     onKeyDown : function(e){
32065         if(this.isVisible()){
32066             this.fireEvent("keydown", this, e);
32067         }
32068     },
32069
32070     /**
32071      * Resizes the dialog.
32072      * @param {Number} width
32073      * @param {Number} height
32074      * @return {Roo.BasicDialog} this
32075      */
32076     resizeTo : function(width, height){
32077         this.el.setSize(width, height);
32078         this.size = {width: width, height: height};
32079         this.syncBodyHeight();
32080         if(this.fixedcenter){
32081             this.center();
32082         }
32083         if(this.isVisible()){
32084             this.constrainXY();
32085             this.adjustAssets();
32086         }
32087         this.fireEvent("resize", this, width, height);
32088         return this;
32089     },
32090
32091
32092     /**
32093      * Resizes the dialog to fit the specified content size.
32094      * @param {Number} width
32095      * @param {Number} height
32096      * @return {Roo.BasicDialog} this
32097      */
32098     setContentSize : function(w, h){
32099         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32100         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32101         //if(!this.el.isBorderBox()){
32102             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32103             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32104         //}
32105         if(this.tabs){
32106             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32107             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32108         }
32109         this.resizeTo(w, h);
32110         return this;
32111     },
32112
32113     /**
32114      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32115      * executed in response to a particular key being pressed while the dialog is active.
32116      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32117      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32118      * @param {Function} fn The function to call
32119      * @param {Object} scope (optional) The scope of the function
32120      * @return {Roo.BasicDialog} this
32121      */
32122     addKeyListener : function(key, fn, scope){
32123         var keyCode, shift, ctrl, alt;
32124         if(typeof key == "object" && !(key instanceof Array)){
32125             keyCode = key["key"];
32126             shift = key["shift"];
32127             ctrl = key["ctrl"];
32128             alt = key["alt"];
32129         }else{
32130             keyCode = key;
32131         }
32132         var handler = function(dlg, e){
32133             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32134                 var k = e.getKey();
32135                 if(keyCode instanceof Array){
32136                     for(var i = 0, len = keyCode.length; i < len; i++){
32137                         if(keyCode[i] == k){
32138                           fn.call(scope || window, dlg, k, e);
32139                           return;
32140                         }
32141                     }
32142                 }else{
32143                     if(k == keyCode){
32144                         fn.call(scope || window, dlg, k, e);
32145                     }
32146                 }
32147             }
32148         };
32149         this.on("keydown", handler);
32150         return this;
32151     },
32152
32153     /**
32154      * Returns the TabPanel component (creates it if it doesn't exist).
32155      * Note: If you wish to simply check for the existence of tabs without creating them,
32156      * check for a null 'tabs' property.
32157      * @return {Roo.TabPanel} The tabs component
32158      */
32159     getTabs : function(){
32160         if(!this.tabs){
32161             this.el.addClass("x-dlg-auto-tabs");
32162             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32163             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32164         }
32165         return this.tabs;
32166     },
32167
32168     /**
32169      * Adds a button to the footer section of the dialog.
32170      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32171      * object or a valid Roo.DomHelper element config
32172      * @param {Function} handler The function called when the button is clicked
32173      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32174      * @return {Roo.Button} The new button
32175      */
32176     addButton : function(config, handler, scope){
32177         var dh = Roo.DomHelper;
32178         if(!this.footer){
32179             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32180         }
32181         if(!this.btnContainer){
32182             var tb = this.footer.createChild({
32183
32184                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32185                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32186             }, null, true);
32187             this.btnContainer = tb.firstChild.firstChild.firstChild;
32188         }
32189         var bconfig = {
32190             handler: handler,
32191             scope: scope,
32192             minWidth: this.minButtonWidth,
32193             hideParent:true
32194         };
32195         if(typeof config == "string"){
32196             bconfig.text = config;
32197         }else{
32198             if(config.tag){
32199                 bconfig.dhconfig = config;
32200             }else{
32201                 Roo.apply(bconfig, config);
32202             }
32203         }
32204         var fc = false;
32205         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32206             bconfig.position = Math.max(0, bconfig.position);
32207             fc = this.btnContainer.childNodes[bconfig.position];
32208         }
32209          
32210         var btn = new Roo.Button(
32211             fc ? 
32212                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32213                 : this.btnContainer.appendChild(document.createElement("td")),
32214             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32215             bconfig
32216         );
32217         this.syncBodyHeight();
32218         if(!this.buttons){
32219             /**
32220              * Array of all the buttons that have been added to this dialog via addButton
32221              * @type Array
32222              */
32223             this.buttons = [];
32224         }
32225         this.buttons.push(btn);
32226         return btn;
32227     },
32228
32229     /**
32230      * Sets the default button to be focused when the dialog is displayed.
32231      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32232      * @return {Roo.BasicDialog} this
32233      */
32234     setDefaultButton : function(btn){
32235         this.defaultButton = btn;
32236         return this;
32237     },
32238
32239     // private
32240     getHeaderFooterHeight : function(safe){
32241         var height = 0;
32242         if(this.header){
32243            height += this.header.getHeight();
32244         }
32245         if(this.footer){
32246            var fm = this.footer.getMargins();
32247             height += (this.footer.getHeight()+fm.top+fm.bottom);
32248         }
32249         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32250         height += this.centerBg.getPadding("tb");
32251         return height;
32252     },
32253
32254     // private
32255     syncBodyHeight : function()
32256     {
32257         var bd = this.body, // the text
32258             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32259             bw = this.bwrap;
32260         var height = this.size.height - this.getHeaderFooterHeight(false);
32261         bd.setHeight(height-bd.getMargins("tb"));
32262         var hh = this.header.getHeight();
32263         var h = this.size.height-hh;
32264         cb.setHeight(h);
32265         
32266         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32267         bw.setHeight(h-cb.getPadding("tb"));
32268         
32269         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32270         bd.setWidth(bw.getWidth(true));
32271         if(this.tabs){
32272             this.tabs.syncHeight();
32273             if(Roo.isIE){
32274                 this.tabs.el.repaint();
32275             }
32276         }
32277     },
32278
32279     /**
32280      * Restores the previous state of the dialog if Roo.state is configured.
32281      * @return {Roo.BasicDialog} this
32282      */
32283     restoreState : function(){
32284         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32285         if(box && box.width){
32286             this.xy = [box.x, box.y];
32287             this.resizeTo(box.width, box.height);
32288         }
32289         return this;
32290     },
32291
32292     // private
32293     beforeShow : function(){
32294         this.expand();
32295         if(this.fixedcenter){
32296             this.xy = this.el.getCenterXY(true);
32297         }
32298         if(this.modal){
32299             Roo.get(document.body).addClass("x-body-masked");
32300             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32301             this.mask.show();
32302         }
32303         this.constrainXY();
32304     },
32305
32306     // private
32307     animShow : function(){
32308         var b = Roo.get(this.animateTarget).getBox();
32309         this.proxy.setSize(b.width, b.height);
32310         this.proxy.setLocation(b.x, b.y);
32311         this.proxy.show();
32312         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32313                     true, .35, this.showEl.createDelegate(this));
32314     },
32315
32316     /**
32317      * Shows the dialog.
32318      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32319      * @return {Roo.BasicDialog} this
32320      */
32321     show : function(animateTarget){
32322         if (this.fireEvent("beforeshow", this) === false){
32323             return;
32324         }
32325         if(this.syncHeightBeforeShow){
32326             this.syncBodyHeight();
32327         }else if(this.firstShow){
32328             this.firstShow = false;
32329             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32330         }
32331         this.animateTarget = animateTarget || this.animateTarget;
32332         if(!this.el.isVisible()){
32333             this.beforeShow();
32334             if(this.animateTarget && Roo.get(this.animateTarget)){
32335                 this.animShow();
32336             }else{
32337                 this.showEl();
32338             }
32339         }
32340         return this;
32341     },
32342
32343     // private
32344     showEl : function(){
32345         this.proxy.hide();
32346         this.el.setXY(this.xy);
32347         this.el.show();
32348         this.adjustAssets(true);
32349         this.toFront();
32350         this.focus();
32351         // IE peekaboo bug - fix found by Dave Fenwick
32352         if(Roo.isIE){
32353             this.el.repaint();
32354         }
32355         this.fireEvent("show", this);
32356     },
32357
32358     /**
32359      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32360      * dialog itself will receive focus.
32361      */
32362     focus : function(){
32363         if(this.defaultButton){
32364             this.defaultButton.focus();
32365         }else{
32366             this.focusEl.focus();
32367         }
32368     },
32369
32370     // private
32371     constrainXY : function(){
32372         if(this.constraintoviewport !== false){
32373             if(!this.viewSize){
32374                 if(this.container){
32375                     var s = this.container.getSize();
32376                     this.viewSize = [s.width, s.height];
32377                 }else{
32378                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32379                 }
32380             }
32381             var s = Roo.get(this.container||document).getScroll();
32382
32383             var x = this.xy[0], y = this.xy[1];
32384             var w = this.size.width, h = this.size.height;
32385             var vw = this.viewSize[0], vh = this.viewSize[1];
32386             // only move it if it needs it
32387             var moved = false;
32388             // first validate right/bottom
32389             if(x + w > vw+s.left){
32390                 x = vw - w;
32391                 moved = true;
32392             }
32393             if(y + h > vh+s.top){
32394                 y = vh - h;
32395                 moved = true;
32396             }
32397             // then make sure top/left isn't negative
32398             if(x < s.left){
32399                 x = s.left;
32400                 moved = true;
32401             }
32402             if(y < s.top){
32403                 y = s.top;
32404                 moved = true;
32405             }
32406             if(moved){
32407                 // cache xy
32408                 this.xy = [x, y];
32409                 if(this.isVisible()){
32410                     this.el.setLocation(x, y);
32411                     this.adjustAssets();
32412                 }
32413             }
32414         }
32415     },
32416
32417     // private
32418     onDrag : function(){
32419         if(!this.proxyDrag){
32420             this.xy = this.el.getXY();
32421             this.adjustAssets();
32422         }
32423     },
32424
32425     // private
32426     adjustAssets : function(doShow){
32427         var x = this.xy[0], y = this.xy[1];
32428         var w = this.size.width, h = this.size.height;
32429         if(doShow === true){
32430             if(this.shadow){
32431                 this.shadow.show(this.el);
32432             }
32433             if(this.shim){
32434                 this.shim.show();
32435             }
32436         }
32437         if(this.shadow && this.shadow.isVisible()){
32438             this.shadow.show(this.el);
32439         }
32440         if(this.shim && this.shim.isVisible()){
32441             this.shim.setBounds(x, y, w, h);
32442         }
32443     },
32444
32445     // private
32446     adjustViewport : function(w, h){
32447         if(!w || !h){
32448             w = Roo.lib.Dom.getViewWidth();
32449             h = Roo.lib.Dom.getViewHeight();
32450         }
32451         // cache the size
32452         this.viewSize = [w, h];
32453         if(this.modal && this.mask.isVisible()){
32454             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32455             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32456         }
32457         if(this.isVisible()){
32458             this.constrainXY();
32459         }
32460     },
32461
32462     /**
32463      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32464      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32465      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32466      */
32467     destroy : function(removeEl){
32468         if(this.isVisible()){
32469             this.animateTarget = null;
32470             this.hide();
32471         }
32472         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32473         if(this.tabs){
32474             this.tabs.destroy(removeEl);
32475         }
32476         Roo.destroy(
32477              this.shim,
32478              this.proxy,
32479              this.resizer,
32480              this.close,
32481              this.mask
32482         );
32483         if(this.dd){
32484             this.dd.unreg();
32485         }
32486         if(this.buttons){
32487            for(var i = 0, len = this.buttons.length; i < len; i++){
32488                this.buttons[i].destroy();
32489            }
32490         }
32491         this.el.removeAllListeners();
32492         if(removeEl === true){
32493             this.el.update("");
32494             this.el.remove();
32495         }
32496         Roo.DialogManager.unregister(this);
32497     },
32498
32499     // private
32500     startMove : function(){
32501         if(this.proxyDrag){
32502             this.proxy.show();
32503         }
32504         if(this.constraintoviewport !== false){
32505             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32506         }
32507     },
32508
32509     // private
32510     endMove : function(){
32511         if(!this.proxyDrag){
32512             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32513         }else{
32514             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32515             this.proxy.hide();
32516         }
32517         this.refreshSize();
32518         this.adjustAssets();
32519         this.focus();
32520         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32521     },
32522
32523     /**
32524      * Brings this dialog to the front of any other visible dialogs
32525      * @return {Roo.BasicDialog} this
32526      */
32527     toFront : function(){
32528         Roo.DialogManager.bringToFront(this);
32529         return this;
32530     },
32531
32532     /**
32533      * Sends this dialog to the back (under) of any other visible dialogs
32534      * @return {Roo.BasicDialog} this
32535      */
32536     toBack : function(){
32537         Roo.DialogManager.sendToBack(this);
32538         return this;
32539     },
32540
32541     /**
32542      * Centers this dialog in the viewport
32543      * @return {Roo.BasicDialog} this
32544      */
32545     center : function(){
32546         var xy = this.el.getCenterXY(true);
32547         this.moveTo(xy[0], xy[1]);
32548         return this;
32549     },
32550
32551     /**
32552      * Moves the dialog's top-left corner to the specified point
32553      * @param {Number} x
32554      * @param {Number} y
32555      * @return {Roo.BasicDialog} this
32556      */
32557     moveTo : function(x, y){
32558         this.xy = [x,y];
32559         if(this.isVisible()){
32560             this.el.setXY(this.xy);
32561             this.adjustAssets();
32562         }
32563         return this;
32564     },
32565
32566     /**
32567      * Aligns the dialog to the specified element
32568      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32569      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32570      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32571      * @return {Roo.BasicDialog} this
32572      */
32573     alignTo : function(element, position, offsets){
32574         this.xy = this.el.getAlignToXY(element, position, offsets);
32575         if(this.isVisible()){
32576             this.el.setXY(this.xy);
32577             this.adjustAssets();
32578         }
32579         return this;
32580     },
32581
32582     /**
32583      * Anchors an element to another element and realigns it when the window is resized.
32584      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32585      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32586      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32587      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32588      * is a number, it is used as the buffer delay (defaults to 50ms).
32589      * @return {Roo.BasicDialog} this
32590      */
32591     anchorTo : function(el, alignment, offsets, monitorScroll){
32592         var action = function(){
32593             this.alignTo(el, alignment, offsets);
32594         };
32595         Roo.EventManager.onWindowResize(action, this);
32596         var tm = typeof monitorScroll;
32597         if(tm != 'undefined'){
32598             Roo.EventManager.on(window, 'scroll', action, this,
32599                 {buffer: tm == 'number' ? monitorScroll : 50});
32600         }
32601         action.call(this);
32602         return this;
32603     },
32604
32605     /**
32606      * Returns true if the dialog is visible
32607      * @return {Boolean}
32608      */
32609     isVisible : function(){
32610         return this.el.isVisible();
32611     },
32612
32613     // private
32614     animHide : function(callback){
32615         var b = Roo.get(this.animateTarget).getBox();
32616         this.proxy.show();
32617         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32618         this.el.hide();
32619         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32620                     this.hideEl.createDelegate(this, [callback]));
32621     },
32622
32623     /**
32624      * Hides the dialog.
32625      * @param {Function} callback (optional) Function to call when the dialog is hidden
32626      * @return {Roo.BasicDialog} this
32627      */
32628     hide : function(callback){
32629         if (this.fireEvent("beforehide", this) === false){
32630             return;
32631         }
32632         if(this.shadow){
32633             this.shadow.hide();
32634         }
32635         if(this.shim) {
32636           this.shim.hide();
32637         }
32638         // sometimes animateTarget seems to get set.. causing problems...
32639         // this just double checks..
32640         if(this.animateTarget && Roo.get(this.animateTarget)) {
32641            this.animHide(callback);
32642         }else{
32643             this.el.hide();
32644             this.hideEl(callback);
32645         }
32646         return this;
32647     },
32648
32649     // private
32650     hideEl : function(callback){
32651         this.proxy.hide();
32652         if(this.modal){
32653             this.mask.hide();
32654             Roo.get(document.body).removeClass("x-body-masked");
32655         }
32656         this.fireEvent("hide", this);
32657         if(typeof callback == "function"){
32658             callback();
32659         }
32660     },
32661
32662     // private
32663     hideAction : function(){
32664         this.setLeft("-10000px");
32665         this.setTop("-10000px");
32666         this.setStyle("visibility", "hidden");
32667     },
32668
32669     // private
32670     refreshSize : function(){
32671         this.size = this.el.getSize();
32672         this.xy = this.el.getXY();
32673         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32674     },
32675
32676     // private
32677     // z-index is managed by the DialogManager and may be overwritten at any time
32678     setZIndex : function(index){
32679         if(this.modal){
32680             this.mask.setStyle("z-index", index);
32681         }
32682         if(this.shim){
32683             this.shim.setStyle("z-index", ++index);
32684         }
32685         if(this.shadow){
32686             this.shadow.setZIndex(++index);
32687         }
32688         this.el.setStyle("z-index", ++index);
32689         if(this.proxy){
32690             this.proxy.setStyle("z-index", ++index);
32691         }
32692         if(this.resizer){
32693             this.resizer.proxy.setStyle("z-index", ++index);
32694         }
32695
32696         this.lastZIndex = index;
32697     },
32698
32699     /**
32700      * Returns the element for this dialog
32701      * @return {Roo.Element} The underlying dialog Element
32702      */
32703     getEl : function(){
32704         return this.el;
32705     }
32706 });
32707
32708 /**
32709  * @class Roo.DialogManager
32710  * Provides global access to BasicDialogs that have been created and
32711  * support for z-indexing (layering) multiple open dialogs.
32712  */
32713 Roo.DialogManager = function(){
32714     var list = {};
32715     var accessList = [];
32716     var front = null;
32717
32718     // private
32719     var sortDialogs = function(d1, d2){
32720         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32721     };
32722
32723     // private
32724     var orderDialogs = function(){
32725         accessList.sort(sortDialogs);
32726         var seed = Roo.DialogManager.zseed;
32727         for(var i = 0, len = accessList.length; i < len; i++){
32728             var dlg = accessList[i];
32729             if(dlg){
32730                 dlg.setZIndex(seed + (i*10));
32731             }
32732         }
32733     };
32734
32735     return {
32736         /**
32737          * The starting z-index for BasicDialogs (defaults to 9000)
32738          * @type Number The z-index value
32739          */
32740         zseed : 9000,
32741
32742         // private
32743         register : function(dlg){
32744             list[dlg.id] = dlg;
32745             accessList.push(dlg);
32746         },
32747
32748         // private
32749         unregister : function(dlg){
32750             delete list[dlg.id];
32751             var i=0;
32752             var len=0;
32753             if(!accessList.indexOf){
32754                 for(  i = 0, len = accessList.length; i < len; i++){
32755                     if(accessList[i] == dlg){
32756                         accessList.splice(i, 1);
32757                         return;
32758                     }
32759                 }
32760             }else{
32761                  i = accessList.indexOf(dlg);
32762                 if(i != -1){
32763                     accessList.splice(i, 1);
32764                 }
32765             }
32766         },
32767
32768         /**
32769          * Gets a registered dialog by id
32770          * @param {String/Object} id The id of the dialog or a dialog
32771          * @return {Roo.BasicDialog} this
32772          */
32773         get : function(id){
32774             return typeof id == "object" ? id : list[id];
32775         },
32776
32777         /**
32778          * Brings the specified dialog to the front
32779          * @param {String/Object} dlg The id of the dialog or a dialog
32780          * @return {Roo.BasicDialog} this
32781          */
32782         bringToFront : function(dlg){
32783             dlg = this.get(dlg);
32784             if(dlg != front){
32785                 front = dlg;
32786                 dlg._lastAccess = new Date().getTime();
32787                 orderDialogs();
32788             }
32789             return dlg;
32790         },
32791
32792         /**
32793          * Sends the specified dialog to the back
32794          * @param {String/Object} dlg The id of the dialog or a dialog
32795          * @return {Roo.BasicDialog} this
32796          */
32797         sendToBack : function(dlg){
32798             dlg = this.get(dlg);
32799             dlg._lastAccess = -(new Date().getTime());
32800             orderDialogs();
32801             return dlg;
32802         },
32803
32804         /**
32805          * Hides all dialogs
32806          */
32807         hideAll : function(){
32808             for(var id in list){
32809                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32810                     list[id].hide();
32811                 }
32812             }
32813         }
32814     };
32815 }();
32816
32817 /**
32818  * @class Roo.LayoutDialog
32819  * @extends Roo.BasicDialog
32820  * Dialog which provides adjustments for working with a layout in a Dialog.
32821  * Add your necessary layout config options to the dialog's config.<br>
32822  * Example usage (including a nested layout):
32823  * <pre><code>
32824 if(!dialog){
32825     dialog = new Roo.LayoutDialog("download-dlg", {
32826         modal: true,
32827         width:600,
32828         height:450,
32829         shadow:true,
32830         minWidth:500,
32831         minHeight:350,
32832         autoTabs:true,
32833         proxyDrag:true,
32834         // layout config merges with the dialog config
32835         center:{
32836             tabPosition: "top",
32837             alwaysShowTabs: true
32838         }
32839     });
32840     dialog.addKeyListener(27, dialog.hide, dialog);
32841     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32842     dialog.addButton("Build It!", this.getDownload, this);
32843
32844     // we can even add nested layouts
32845     var innerLayout = new Roo.BorderLayout("dl-inner", {
32846         east: {
32847             initialSize: 200,
32848             autoScroll:true,
32849             split:true
32850         },
32851         center: {
32852             autoScroll:true
32853         }
32854     });
32855     innerLayout.beginUpdate();
32856     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
32857     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
32858     innerLayout.endUpdate(true);
32859
32860     var layout = dialog.getLayout();
32861     layout.beginUpdate();
32862     layout.add("center", new Roo.ContentPanel("standard-panel",
32863                         {title: "Download the Source", fitToFrame:true}));
32864     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
32865                {title: "Build your own roo.js"}));
32866     layout.getRegion("center").showPanel(sp);
32867     layout.endUpdate();
32868 }
32869 </code></pre>
32870     * @constructor
32871     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
32872     * @param {Object} config configuration options
32873   */
32874 Roo.LayoutDialog = function(el, cfg){
32875     
32876     var config=  cfg;
32877     if (typeof(cfg) == 'undefined') {
32878         config = Roo.apply({}, el);
32879         // not sure why we use documentElement here.. - it should always be body.
32880         // IE7 borks horribly if we use documentElement.
32881         // webkit also does not like documentElement - it creates a body element...
32882         el = Roo.get( document.body || document.documentElement ).createChild();
32883         //config.autoCreate = true;
32884     }
32885     
32886     
32887     config.autoTabs = false;
32888     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
32889     this.body.setStyle({overflow:"hidden", position:"relative"});
32890     this.layout = new Roo.BorderLayout(this.body.dom, config);
32891     this.layout.monitorWindowResize = false;
32892     this.el.addClass("x-dlg-auto-layout");
32893     // fix case when center region overwrites center function
32894     this.center = Roo.BasicDialog.prototype.center;
32895     this.on("show", this.layout.layout, this.layout, true);
32896     if (config.items) {
32897         var xitems = config.items;
32898         delete config.items;
32899         Roo.each(xitems, this.addxtype, this);
32900     }
32901     
32902     
32903 };
32904 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
32905     /**
32906      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
32907      * @deprecated
32908      */
32909     endUpdate : function(){
32910         this.layout.endUpdate();
32911     },
32912
32913     /**
32914      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
32915      *  @deprecated
32916      */
32917     beginUpdate : function(){
32918         this.layout.beginUpdate();
32919     },
32920
32921     /**
32922      * Get the BorderLayout for this dialog
32923      * @return {Roo.BorderLayout}
32924      */
32925     getLayout : function(){
32926         return this.layout;
32927     },
32928
32929     showEl : function(){
32930         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
32931         if(Roo.isIE7){
32932             this.layout.layout();
32933         }
32934     },
32935
32936     // private
32937     // Use the syncHeightBeforeShow config option to control this automatically
32938     syncBodyHeight : function(){
32939         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
32940         if(this.layout){this.layout.layout();}
32941     },
32942     
32943       /**
32944      * Add an xtype element (actually adds to the layout.)
32945      * @return {Object} xdata xtype object data.
32946      */
32947     
32948     addxtype : function(c) {
32949         return this.layout.addxtype(c);
32950     }
32951 });/*
32952  * Based on:
32953  * Ext JS Library 1.1.1
32954  * Copyright(c) 2006-2007, Ext JS, LLC.
32955  *
32956  * Originally Released Under LGPL - original licence link has changed is not relivant.
32957  *
32958  * Fork - LGPL
32959  * <script type="text/javascript">
32960  */
32961  
32962 /**
32963  * @class Roo.MessageBox
32964  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
32965  * Example usage:
32966  *<pre><code>
32967 // Basic alert:
32968 Roo.Msg.alert('Status', 'Changes saved successfully.');
32969
32970 // Prompt for user data:
32971 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
32972     if (btn == 'ok'){
32973         // process text value...
32974     }
32975 });
32976
32977 // Show a dialog using config options:
32978 Roo.Msg.show({
32979    title:'Save Changes?',
32980    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
32981    buttons: Roo.Msg.YESNOCANCEL,
32982    fn: processResult,
32983    animEl: 'elId'
32984 });
32985 </code></pre>
32986  * @singleton
32987  */
32988 Roo.MessageBox = function(){
32989     var dlg, opt, mask, waitTimer;
32990     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
32991     var buttons, activeTextEl, bwidth;
32992
32993     // private
32994     var handleButton = function(button){
32995         dlg.hide();
32996         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
32997     };
32998
32999     // private
33000     var handleHide = function(){
33001         if(opt && opt.cls){
33002             dlg.el.removeClass(opt.cls);
33003         }
33004         if(waitTimer){
33005             Roo.TaskMgr.stop(waitTimer);
33006             waitTimer = null;
33007         }
33008     };
33009
33010     // private
33011     var updateButtons = function(b){
33012         var width = 0;
33013         if(!b){
33014             buttons["ok"].hide();
33015             buttons["cancel"].hide();
33016             buttons["yes"].hide();
33017             buttons["no"].hide();
33018             dlg.footer.dom.style.display = 'none';
33019             return width;
33020         }
33021         dlg.footer.dom.style.display = '';
33022         for(var k in buttons){
33023             if(typeof buttons[k] != "function"){
33024                 if(b[k]){
33025                     buttons[k].show();
33026                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33027                     width += buttons[k].el.getWidth()+15;
33028                 }else{
33029                     buttons[k].hide();
33030                 }
33031             }
33032         }
33033         return width;
33034     };
33035
33036     // private
33037     var handleEsc = function(d, k, e){
33038         if(opt && opt.closable !== false){
33039             dlg.hide();
33040         }
33041         if(e){
33042             e.stopEvent();
33043         }
33044     };
33045
33046     return {
33047         /**
33048          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33049          * @return {Roo.BasicDialog} The BasicDialog element
33050          */
33051         getDialog : function(){
33052            if(!dlg){
33053                 dlg = new Roo.BasicDialog("x-msg-box", {
33054                     autoCreate : true,
33055                     shadow: true,
33056                     draggable: true,
33057                     resizable:false,
33058                     constraintoviewport:false,
33059                     fixedcenter:true,
33060                     collapsible : false,
33061                     shim:true,
33062                     modal: true,
33063                     width:400, height:100,
33064                     buttonAlign:"center",
33065                     closeClick : function(){
33066                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33067                             handleButton("no");
33068                         }else{
33069                             handleButton("cancel");
33070                         }
33071                     }
33072                 });
33073                 dlg.on("hide", handleHide);
33074                 mask = dlg.mask;
33075                 dlg.addKeyListener(27, handleEsc);
33076                 buttons = {};
33077                 var bt = this.buttonText;
33078                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33079                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33080                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33081                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33082                 bodyEl = dlg.body.createChild({
33083
33084                     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>'
33085                 });
33086                 msgEl = bodyEl.dom.firstChild;
33087                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33088                 textboxEl.enableDisplayMode();
33089                 textboxEl.addKeyListener([10,13], function(){
33090                     if(dlg.isVisible() && opt && opt.buttons){
33091                         if(opt.buttons.ok){
33092                             handleButton("ok");
33093                         }else if(opt.buttons.yes){
33094                             handleButton("yes");
33095                         }
33096                     }
33097                 });
33098                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33099                 textareaEl.enableDisplayMode();
33100                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33101                 progressEl.enableDisplayMode();
33102                 var pf = progressEl.dom.firstChild;
33103                 if (pf) {
33104                     pp = Roo.get(pf.firstChild);
33105                     pp.setHeight(pf.offsetHeight);
33106                 }
33107                 
33108             }
33109             return dlg;
33110         },
33111
33112         /**
33113          * Updates the message box body text
33114          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33115          * the XHTML-compliant non-breaking space character '&amp;#160;')
33116          * @return {Roo.MessageBox} This message box
33117          */
33118         updateText : function(text){
33119             if(!dlg.isVisible() && !opt.width){
33120                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33121             }
33122             msgEl.innerHTML = text || '&#160;';
33123       
33124             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33125             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33126             var w = Math.max(
33127                     Math.min(opt.width || cw , this.maxWidth), 
33128                     Math.max(opt.minWidth || this.minWidth, bwidth)
33129             );
33130             if(opt.prompt){
33131                 activeTextEl.setWidth(w);
33132             }
33133             if(dlg.isVisible()){
33134                 dlg.fixedcenter = false;
33135             }
33136             // to big, make it scroll. = But as usual stupid IE does not support
33137             // !important..
33138             
33139             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33140                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33141                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33142             } else {
33143                 bodyEl.dom.style.height = '';
33144                 bodyEl.dom.style.overflowY = '';
33145             }
33146             if (cw > w) {
33147                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33148             } else {
33149                 bodyEl.dom.style.overflowX = '';
33150             }
33151             
33152             dlg.setContentSize(w, bodyEl.getHeight());
33153             if(dlg.isVisible()){
33154                 dlg.fixedcenter = true;
33155             }
33156             return this;
33157         },
33158
33159         /**
33160          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33161          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33162          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33163          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33164          * @return {Roo.MessageBox} This message box
33165          */
33166         updateProgress : function(value, text){
33167             if(text){
33168                 this.updateText(text);
33169             }
33170             if (pp) { // weird bug on my firefox - for some reason this is not defined
33171                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33172             }
33173             return this;
33174         },        
33175
33176         /**
33177          * Returns true if the message box is currently displayed
33178          * @return {Boolean} True if the message box is visible, else false
33179          */
33180         isVisible : function(){
33181             return dlg && dlg.isVisible();  
33182         },
33183
33184         /**
33185          * Hides the message box if it is displayed
33186          */
33187         hide : function(){
33188             if(this.isVisible()){
33189                 dlg.hide();
33190             }  
33191         },
33192
33193         /**
33194          * Displays a new message box, or reinitializes an existing message box, based on the config options
33195          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33196          * The following config object properties are supported:
33197          * <pre>
33198 Property    Type             Description
33199 ----------  ---------------  ------------------------------------------------------------------------------------
33200 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33201                                    closes (defaults to undefined)
33202 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33203                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33204 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33205                                    progress and wait dialogs will ignore this property and always hide the
33206                                    close button as they can only be closed programmatically.
33207 cls               String           A custom CSS class to apply to the message box element
33208 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33209                                    displayed (defaults to 75)
33210 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33211                                    function will be btn (the name of the button that was clicked, if applicable,
33212                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33213                                    Progress and wait dialogs will ignore this option since they do not respond to
33214                                    user actions and can only be closed programmatically, so any required function
33215                                    should be called by the same code after it closes the dialog.
33216 icon              String           A CSS class that provides a background image to be used as an icon for
33217                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33218 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33219 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33220 modal             Boolean          False to allow user interaction with the page while the message box is
33221                                    displayed (defaults to true)
33222 msg               String           A string that will replace the existing message box body text (defaults
33223                                    to the XHTML-compliant non-breaking space character '&#160;')
33224 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33225 progress          Boolean          True to display a progress bar (defaults to false)
33226 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33227 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33228 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33229 title             String           The title text
33230 value             String           The string value to set into the active textbox element if displayed
33231 wait              Boolean          True to display a progress bar (defaults to false)
33232 width             Number           The width of the dialog in pixels
33233 </pre>
33234          *
33235          * Example usage:
33236          * <pre><code>
33237 Roo.Msg.show({
33238    title: 'Address',
33239    msg: 'Please enter your address:',
33240    width: 300,
33241    buttons: Roo.MessageBox.OKCANCEL,
33242    multiline: true,
33243    fn: saveAddress,
33244    animEl: 'addAddressBtn'
33245 });
33246 </code></pre>
33247          * @param {Object} config Configuration options
33248          * @return {Roo.MessageBox} This message box
33249          */
33250         show : function(options)
33251         {
33252             
33253             // this causes nightmares if you show one dialog after another
33254             // especially on callbacks..
33255              
33256             if(this.isVisible()){
33257                 
33258                 this.hide();
33259                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33260                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33261                 Roo.log("New Dialog Message:" +  options.msg )
33262                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33263                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33264                 
33265             }
33266             var d = this.getDialog();
33267             opt = options;
33268             d.setTitle(opt.title || "&#160;");
33269             d.close.setDisplayed(opt.closable !== false);
33270             activeTextEl = textboxEl;
33271             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33272             if(opt.prompt){
33273                 if(opt.multiline){
33274                     textboxEl.hide();
33275                     textareaEl.show();
33276                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33277                         opt.multiline : this.defaultTextHeight);
33278                     activeTextEl = textareaEl;
33279                 }else{
33280                     textboxEl.show();
33281                     textareaEl.hide();
33282                 }
33283             }else{
33284                 textboxEl.hide();
33285                 textareaEl.hide();
33286             }
33287             progressEl.setDisplayed(opt.progress === true);
33288             this.updateProgress(0);
33289             activeTextEl.dom.value = opt.value || "";
33290             if(opt.prompt){
33291                 dlg.setDefaultButton(activeTextEl);
33292             }else{
33293                 var bs = opt.buttons;
33294                 var db = null;
33295                 if(bs && bs.ok){
33296                     db = buttons["ok"];
33297                 }else if(bs && bs.yes){
33298                     db = buttons["yes"];
33299                 }
33300                 dlg.setDefaultButton(db);
33301             }
33302             bwidth = updateButtons(opt.buttons);
33303             this.updateText(opt.msg);
33304             if(opt.cls){
33305                 d.el.addClass(opt.cls);
33306             }
33307             d.proxyDrag = opt.proxyDrag === true;
33308             d.modal = opt.modal !== false;
33309             d.mask = opt.modal !== false ? mask : false;
33310             if(!d.isVisible()){
33311                 // force it to the end of the z-index stack so it gets a cursor in FF
33312                 document.body.appendChild(dlg.el.dom);
33313                 d.animateTarget = null;
33314                 d.show(options.animEl);
33315             }
33316             return this;
33317         },
33318
33319         /**
33320          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33321          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33322          * and closing the message box when the process is complete.
33323          * @param {String} title The title bar text
33324          * @param {String} msg The message box body text
33325          * @return {Roo.MessageBox} This message box
33326          */
33327         progress : function(title, msg){
33328             this.show({
33329                 title : title,
33330                 msg : msg,
33331                 buttons: false,
33332                 progress:true,
33333                 closable:false,
33334                 minWidth: this.minProgressWidth,
33335                 modal : true
33336             });
33337             return this;
33338         },
33339
33340         /**
33341          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33342          * If a callback function is passed it will be called after the user clicks the button, and the
33343          * id of the button that was clicked will be passed as the only parameter to the callback
33344          * (could also be the top-right close button).
33345          * @param {String} title The title bar text
33346          * @param {String} msg The message box body text
33347          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33348          * @param {Object} scope (optional) The scope of the callback function
33349          * @return {Roo.MessageBox} This message box
33350          */
33351         alert : function(title, msg, fn, scope){
33352             this.show({
33353                 title : title,
33354                 msg : msg,
33355                 buttons: this.OK,
33356                 fn: fn,
33357                 scope : scope,
33358                 modal : true
33359             });
33360             return this;
33361         },
33362
33363         /**
33364          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33365          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33366          * You are responsible for closing the message box when the process is complete.
33367          * @param {String} msg The message box body text
33368          * @param {String} title (optional) The title bar text
33369          * @return {Roo.MessageBox} This message box
33370          */
33371         wait : function(msg, title){
33372             this.show({
33373                 title : title,
33374                 msg : msg,
33375                 buttons: false,
33376                 closable:false,
33377                 progress:true,
33378                 modal:true,
33379                 width:300,
33380                 wait:true
33381             });
33382             waitTimer = Roo.TaskMgr.start({
33383                 run: function(i){
33384                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33385                 },
33386                 interval: 1000
33387             });
33388             return this;
33389         },
33390
33391         /**
33392          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33393          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33394          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33395          * @param {String} title The title bar text
33396          * @param {String} msg The message box body text
33397          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33398          * @param {Object} scope (optional) The scope of the callback function
33399          * @return {Roo.MessageBox} This message box
33400          */
33401         confirm : function(title, msg, fn, scope){
33402             this.show({
33403                 title : title,
33404                 msg : msg,
33405                 buttons: this.YESNO,
33406                 fn: fn,
33407                 scope : scope,
33408                 modal : true
33409             });
33410             return this;
33411         },
33412
33413         /**
33414          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33415          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33416          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33417          * (could also be the top-right close button) and the text that was entered will be passed as the two
33418          * parameters to the callback.
33419          * @param {String} title The title bar text
33420          * @param {String} msg The message box body text
33421          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33422          * @param {Object} scope (optional) The scope of the callback function
33423          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33424          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33425          * @return {Roo.MessageBox} This message box
33426          */
33427         prompt : function(title, msg, fn, scope, multiline){
33428             this.show({
33429                 title : title,
33430                 msg : msg,
33431                 buttons: this.OKCANCEL,
33432                 fn: fn,
33433                 minWidth:250,
33434                 scope : scope,
33435                 prompt:true,
33436                 multiline: multiline,
33437                 modal : true
33438             });
33439             return this;
33440         },
33441
33442         /**
33443          * Button config that displays a single OK button
33444          * @type Object
33445          */
33446         OK : {ok:true},
33447         /**
33448          * Button config that displays Yes and No buttons
33449          * @type Object
33450          */
33451         YESNO : {yes:true, no:true},
33452         /**
33453          * Button config that displays OK and Cancel buttons
33454          * @type Object
33455          */
33456         OKCANCEL : {ok:true, cancel:true},
33457         /**
33458          * Button config that displays Yes, No and Cancel buttons
33459          * @type Object
33460          */
33461         YESNOCANCEL : {yes:true, no:true, cancel:true},
33462
33463         /**
33464          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33465          * @type Number
33466          */
33467         defaultTextHeight : 75,
33468         /**
33469          * The maximum width in pixels of the message box (defaults to 600)
33470          * @type Number
33471          */
33472         maxWidth : 600,
33473         /**
33474          * The minimum width in pixels of the message box (defaults to 100)
33475          * @type Number
33476          */
33477         minWidth : 100,
33478         /**
33479          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33480          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33481          * @type Number
33482          */
33483         minProgressWidth : 250,
33484         /**
33485          * An object containing the default button text strings that can be overriden for localized language support.
33486          * Supported properties are: ok, cancel, yes and no.
33487          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33488          * @type Object
33489          */
33490         buttonText : {
33491             ok : "OK",
33492             cancel : "Cancel",
33493             yes : "Yes",
33494             no : "No"
33495         }
33496     };
33497 }();
33498
33499 /**
33500  * Shorthand for {@link Roo.MessageBox}
33501  */
33502 Roo.Msg = Roo.MessageBox;/*
33503  * Based on:
33504  * Ext JS Library 1.1.1
33505  * Copyright(c) 2006-2007, Ext JS, LLC.
33506  *
33507  * Originally Released Under LGPL - original licence link has changed is not relivant.
33508  *
33509  * Fork - LGPL
33510  * <script type="text/javascript">
33511  */
33512 /**
33513  * @class Roo.QuickTips
33514  * Provides attractive and customizable tooltips for any element.
33515  * @singleton
33516  */
33517 Roo.QuickTips = function(){
33518     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33519     var ce, bd, xy, dd;
33520     var visible = false, disabled = true, inited = false;
33521     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33522     
33523     var onOver = function(e){
33524         if(disabled){
33525             return;
33526         }
33527         var t = e.getTarget();
33528         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33529             return;
33530         }
33531         if(ce && t == ce.el){
33532             clearTimeout(hideProc);
33533             return;
33534         }
33535         if(t && tagEls[t.id]){
33536             tagEls[t.id].el = t;
33537             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33538             return;
33539         }
33540         var ttp, et = Roo.fly(t);
33541         var ns = cfg.namespace;
33542         if(tm.interceptTitles && t.title){
33543             ttp = t.title;
33544             t.qtip = ttp;
33545             t.removeAttribute("title");
33546             e.preventDefault();
33547         }else{
33548             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33549         }
33550         if(ttp){
33551             showProc = show.defer(tm.showDelay, tm, [{
33552                 el: t, 
33553                 text: ttp, 
33554                 width: et.getAttributeNS(ns, cfg.width),
33555                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33556                 title: et.getAttributeNS(ns, cfg.title),
33557                     cls: et.getAttributeNS(ns, cfg.cls)
33558             }]);
33559         }
33560     };
33561     
33562     var onOut = function(e){
33563         clearTimeout(showProc);
33564         var t = e.getTarget();
33565         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33566             hideProc = setTimeout(hide, tm.hideDelay);
33567         }
33568     };
33569     
33570     var onMove = function(e){
33571         if(disabled){
33572             return;
33573         }
33574         xy = e.getXY();
33575         xy[1] += 18;
33576         if(tm.trackMouse && ce){
33577             el.setXY(xy);
33578         }
33579     };
33580     
33581     var onDown = function(e){
33582         clearTimeout(showProc);
33583         clearTimeout(hideProc);
33584         if(!e.within(el)){
33585             if(tm.hideOnClick){
33586                 hide();
33587                 tm.disable();
33588                 tm.enable.defer(100, tm);
33589             }
33590         }
33591     };
33592     
33593     var getPad = function(){
33594         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33595     };
33596
33597     var show = function(o){
33598         if(disabled){
33599             return;
33600         }
33601         clearTimeout(dismissProc);
33602         ce = o;
33603         if(removeCls){ // in case manually hidden
33604             el.removeClass(removeCls);
33605             removeCls = null;
33606         }
33607         if(ce.cls){
33608             el.addClass(ce.cls);
33609             removeCls = ce.cls;
33610         }
33611         if(ce.title){
33612             tipTitle.update(ce.title);
33613             tipTitle.show();
33614         }else{
33615             tipTitle.update('');
33616             tipTitle.hide();
33617         }
33618         el.dom.style.width  = tm.maxWidth+'px';
33619         //tipBody.dom.style.width = '';
33620         tipBodyText.update(o.text);
33621         var p = getPad(), w = ce.width;
33622         if(!w){
33623             var td = tipBodyText.dom;
33624             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33625             if(aw > tm.maxWidth){
33626                 w = tm.maxWidth;
33627             }else if(aw < tm.minWidth){
33628                 w = tm.minWidth;
33629             }else{
33630                 w = aw;
33631             }
33632         }
33633         //tipBody.setWidth(w);
33634         el.setWidth(parseInt(w, 10) + p);
33635         if(ce.autoHide === false){
33636             close.setDisplayed(true);
33637             if(dd){
33638                 dd.unlock();
33639             }
33640         }else{
33641             close.setDisplayed(false);
33642             if(dd){
33643                 dd.lock();
33644             }
33645         }
33646         if(xy){
33647             el.avoidY = xy[1]-18;
33648             el.setXY(xy);
33649         }
33650         if(tm.animate){
33651             el.setOpacity(.1);
33652             el.setStyle("visibility", "visible");
33653             el.fadeIn({callback: afterShow});
33654         }else{
33655             afterShow();
33656         }
33657     };
33658     
33659     var afterShow = function(){
33660         if(ce){
33661             el.show();
33662             esc.enable();
33663             if(tm.autoDismiss && ce.autoHide !== false){
33664                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33665             }
33666         }
33667     };
33668     
33669     var hide = function(noanim){
33670         clearTimeout(dismissProc);
33671         clearTimeout(hideProc);
33672         ce = null;
33673         if(el.isVisible()){
33674             esc.disable();
33675             if(noanim !== true && tm.animate){
33676                 el.fadeOut({callback: afterHide});
33677             }else{
33678                 afterHide();
33679             } 
33680         }
33681     };
33682     
33683     var afterHide = function(){
33684         el.hide();
33685         if(removeCls){
33686             el.removeClass(removeCls);
33687             removeCls = null;
33688         }
33689     };
33690     
33691     return {
33692         /**
33693         * @cfg {Number} minWidth
33694         * The minimum width of the quick tip (defaults to 40)
33695         */
33696        minWidth : 40,
33697         /**
33698         * @cfg {Number} maxWidth
33699         * The maximum width of the quick tip (defaults to 300)
33700         */
33701        maxWidth : 300,
33702         /**
33703         * @cfg {Boolean} interceptTitles
33704         * True to automatically use the element's DOM title value if available (defaults to false)
33705         */
33706        interceptTitles : false,
33707         /**
33708         * @cfg {Boolean} trackMouse
33709         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33710         */
33711        trackMouse : false,
33712         /**
33713         * @cfg {Boolean} hideOnClick
33714         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33715         */
33716        hideOnClick : true,
33717         /**
33718         * @cfg {Number} showDelay
33719         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33720         */
33721        showDelay : 500,
33722         /**
33723         * @cfg {Number} hideDelay
33724         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33725         */
33726        hideDelay : 200,
33727         /**
33728         * @cfg {Boolean} autoHide
33729         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33730         * Used in conjunction with hideDelay.
33731         */
33732        autoHide : true,
33733         /**
33734         * @cfg {Boolean}
33735         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33736         * (defaults to true).  Used in conjunction with autoDismissDelay.
33737         */
33738        autoDismiss : true,
33739         /**
33740         * @cfg {Number}
33741         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33742         */
33743        autoDismissDelay : 5000,
33744        /**
33745         * @cfg {Boolean} animate
33746         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33747         */
33748        animate : false,
33749
33750        /**
33751         * @cfg {String} title
33752         * Title text to display (defaults to '').  This can be any valid HTML markup.
33753         */
33754         title: '',
33755        /**
33756         * @cfg {String} text
33757         * Body text to display (defaults to '').  This can be any valid HTML markup.
33758         */
33759         text : '',
33760        /**
33761         * @cfg {String} cls
33762         * A CSS class to apply to the base quick tip element (defaults to '').
33763         */
33764         cls : '',
33765        /**
33766         * @cfg {Number} width
33767         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33768         * minWidth or maxWidth.
33769         */
33770         width : null,
33771
33772     /**
33773      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33774      * or display QuickTips in a page.
33775      */
33776        init : function(){
33777           tm = Roo.QuickTips;
33778           cfg = tm.tagConfig;
33779           if(!inited){
33780               if(!Roo.isReady){ // allow calling of init() before onReady
33781                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33782                   return;
33783               }
33784               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33785               el.fxDefaults = {stopFx: true};
33786               // maximum custom styling
33787               //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>');
33788               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>');              
33789               tipTitle = el.child('h3');
33790               tipTitle.enableDisplayMode("block");
33791               tipBody = el.child('div.x-tip-bd');
33792               tipBodyText = el.child('div.x-tip-bd-inner');
33793               //bdLeft = el.child('div.x-tip-bd-left');
33794               //bdRight = el.child('div.x-tip-bd-right');
33795               close = el.child('div.x-tip-close');
33796               close.enableDisplayMode("block");
33797               close.on("click", hide);
33798               var d = Roo.get(document);
33799               d.on("mousedown", onDown);
33800               d.on("mouseover", onOver);
33801               d.on("mouseout", onOut);
33802               d.on("mousemove", onMove);
33803               esc = d.addKeyListener(27, hide);
33804               esc.disable();
33805               if(Roo.dd.DD){
33806                   dd = el.initDD("default", null, {
33807                       onDrag : function(){
33808                           el.sync();  
33809                       }
33810                   });
33811                   dd.setHandleElId(tipTitle.id);
33812                   dd.lock();
33813               }
33814               inited = true;
33815           }
33816           this.enable(); 
33817        },
33818
33819     /**
33820      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33821      * are supported:
33822      * <pre>
33823 Property    Type                   Description
33824 ----------  ---------------------  ------------------------------------------------------------------------
33825 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33826      * </ul>
33827      * @param {Object} config The config object
33828      */
33829        register : function(config){
33830            var cs = config instanceof Array ? config : arguments;
33831            for(var i = 0, len = cs.length; i < len; i++) {
33832                var c = cs[i];
33833                var target = c.target;
33834                if(target){
33835                    if(target instanceof Array){
33836                        for(var j = 0, jlen = target.length; j < jlen; j++){
33837                            tagEls[target[j]] = c;
33838                        }
33839                    }else{
33840                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33841                    }
33842                }
33843            }
33844        },
33845
33846     /**
33847      * Removes this quick tip from its element and destroys it.
33848      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
33849      */
33850        unregister : function(el){
33851            delete tagEls[Roo.id(el)];
33852        },
33853
33854     /**
33855      * Enable this quick tip.
33856      */
33857        enable : function(){
33858            if(inited && disabled){
33859                locks.pop();
33860                if(locks.length < 1){
33861                    disabled = false;
33862                }
33863            }
33864        },
33865
33866     /**
33867      * Disable this quick tip.
33868      */
33869        disable : function(){
33870           disabled = true;
33871           clearTimeout(showProc);
33872           clearTimeout(hideProc);
33873           clearTimeout(dismissProc);
33874           if(ce){
33875               hide(true);
33876           }
33877           locks.push(1);
33878        },
33879
33880     /**
33881      * Returns true if the quick tip is enabled, else false.
33882      */
33883        isEnabled : function(){
33884             return !disabled;
33885        },
33886
33887         // private
33888        tagConfig : {
33889            namespace : "roo", // was ext?? this may break..
33890            alt_namespace : "ext",
33891            attribute : "qtip",
33892            width : "width",
33893            target : "target",
33894            title : "qtitle",
33895            hide : "hide",
33896            cls : "qclass"
33897        }
33898    };
33899 }();
33900
33901 // backwards compat
33902 Roo.QuickTips.tips = Roo.QuickTips.register;/*
33903  * Based on:
33904  * Ext JS Library 1.1.1
33905  * Copyright(c) 2006-2007, Ext JS, LLC.
33906  *
33907  * Originally Released Under LGPL - original licence link has changed is not relivant.
33908  *
33909  * Fork - LGPL
33910  * <script type="text/javascript">
33911  */
33912  
33913
33914 /**
33915  * @class Roo.tree.TreePanel
33916  * @extends Roo.data.Tree
33917
33918  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
33919  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
33920  * @cfg {Boolean} enableDD true to enable drag and drop
33921  * @cfg {Boolean} enableDrag true to enable just drag
33922  * @cfg {Boolean} enableDrop true to enable just drop
33923  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
33924  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
33925  * @cfg {String} ddGroup The DD group this TreePanel belongs to
33926  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
33927  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
33928  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
33929  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
33930  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
33931  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
33932  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
33933  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
33934  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
33935  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
33936  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
33937  * @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>
33938  * @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>
33939  * 
33940  * @constructor
33941  * @param {String/HTMLElement/Element} el The container element
33942  * @param {Object} config
33943  */
33944 Roo.tree.TreePanel = function(el, config){
33945     var root = false;
33946     var loader = false;
33947     if (config.root) {
33948         root = config.root;
33949         delete config.root;
33950     }
33951     if (config.loader) {
33952         loader = config.loader;
33953         delete config.loader;
33954     }
33955     
33956     Roo.apply(this, config);
33957     Roo.tree.TreePanel.superclass.constructor.call(this);
33958     this.el = Roo.get(el);
33959     this.el.addClass('x-tree');
33960     //console.log(root);
33961     if (root) {
33962         this.setRootNode( Roo.factory(root, Roo.tree));
33963     }
33964     if (loader) {
33965         this.loader = Roo.factory(loader, Roo.tree);
33966     }
33967    /**
33968     * Read-only. The id of the container element becomes this TreePanel's id.
33969     */
33970     this.id = this.el.id;
33971     this.addEvents({
33972         /**
33973         * @event beforeload
33974         * Fires before a node is loaded, return false to cancel
33975         * @param {Node} node The node being loaded
33976         */
33977         "beforeload" : true,
33978         /**
33979         * @event load
33980         * Fires when a node is loaded
33981         * @param {Node} node The node that was loaded
33982         */
33983         "load" : true,
33984         /**
33985         * @event textchange
33986         * Fires when the text for a node is changed
33987         * @param {Node} node The node
33988         * @param {String} text The new text
33989         * @param {String} oldText The old text
33990         */
33991         "textchange" : true,
33992         /**
33993         * @event beforeexpand
33994         * Fires before a node is expanded, return false to cancel.
33995         * @param {Node} node The node
33996         * @param {Boolean} deep
33997         * @param {Boolean} anim
33998         */
33999         "beforeexpand" : true,
34000         /**
34001         * @event beforecollapse
34002         * Fires before a node is collapsed, return false to cancel.
34003         * @param {Node} node The node
34004         * @param {Boolean} deep
34005         * @param {Boolean} anim
34006         */
34007         "beforecollapse" : true,
34008         /**
34009         * @event expand
34010         * Fires when a node is expanded
34011         * @param {Node} node The node
34012         */
34013         "expand" : true,
34014         /**
34015         * @event disabledchange
34016         * Fires when the disabled status of a node changes
34017         * @param {Node} node The node
34018         * @param {Boolean} disabled
34019         */
34020         "disabledchange" : true,
34021         /**
34022         * @event collapse
34023         * Fires when a node is collapsed
34024         * @param {Node} node The node
34025         */
34026         "collapse" : true,
34027         /**
34028         * @event beforeclick
34029         * Fires before click processing on a node. Return false to cancel the default action.
34030         * @param {Node} node The node
34031         * @param {Roo.EventObject} e The event object
34032         */
34033         "beforeclick":true,
34034         /**
34035         * @event checkchange
34036         * Fires when a node with a checkbox's checked property changes
34037         * @param {Node} this This node
34038         * @param {Boolean} checked
34039         */
34040         "checkchange":true,
34041         /**
34042         * @event click
34043         * Fires when a node is clicked
34044         * @param {Node} node The node
34045         * @param {Roo.EventObject} e The event object
34046         */
34047         "click":true,
34048         /**
34049         * @event dblclick
34050         * Fires when a node is double clicked
34051         * @param {Node} node The node
34052         * @param {Roo.EventObject} e The event object
34053         */
34054         "dblclick":true,
34055         /**
34056         * @event contextmenu
34057         * Fires when a node is right clicked
34058         * @param {Node} node The node
34059         * @param {Roo.EventObject} e The event object
34060         */
34061         "contextmenu":true,
34062         /**
34063         * @event beforechildrenrendered
34064         * Fires right before the child nodes for a node are rendered
34065         * @param {Node} node The node
34066         */
34067         "beforechildrenrendered":true,
34068         /**
34069         * @event startdrag
34070         * Fires when a node starts being dragged
34071         * @param {Roo.tree.TreePanel} this
34072         * @param {Roo.tree.TreeNode} node
34073         * @param {event} e The raw browser event
34074         */ 
34075        "startdrag" : true,
34076        /**
34077         * @event enddrag
34078         * Fires when a drag operation is complete
34079         * @param {Roo.tree.TreePanel} this
34080         * @param {Roo.tree.TreeNode} node
34081         * @param {event} e The raw browser event
34082         */
34083        "enddrag" : true,
34084        /**
34085         * @event dragdrop
34086         * Fires when a dragged node is dropped on a valid DD target
34087         * @param {Roo.tree.TreePanel} this
34088         * @param {Roo.tree.TreeNode} node
34089         * @param {DD} dd The dd it was dropped on
34090         * @param {event} e The raw browser event
34091         */
34092        "dragdrop" : true,
34093        /**
34094         * @event beforenodedrop
34095         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34096         * passed to handlers has the following properties:<br />
34097         * <ul style="padding:5px;padding-left:16px;">
34098         * <li>tree - The TreePanel</li>
34099         * <li>target - The node being targeted for the drop</li>
34100         * <li>data - The drag data from the drag source</li>
34101         * <li>point - The point of the drop - append, above or below</li>
34102         * <li>source - The drag source</li>
34103         * <li>rawEvent - Raw mouse event</li>
34104         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34105         * to be inserted by setting them on this object.</li>
34106         * <li>cancel - Set this to true to cancel the drop.</li>
34107         * </ul>
34108         * @param {Object} dropEvent
34109         */
34110        "beforenodedrop" : true,
34111        /**
34112         * @event nodedrop
34113         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34114         * passed to handlers has the following properties:<br />
34115         * <ul style="padding:5px;padding-left:16px;">
34116         * <li>tree - The TreePanel</li>
34117         * <li>target - The node being targeted for the drop</li>
34118         * <li>data - The drag data from the drag source</li>
34119         * <li>point - The point of the drop - append, above or below</li>
34120         * <li>source - The drag source</li>
34121         * <li>rawEvent - Raw mouse event</li>
34122         * <li>dropNode - Dropped node(s).</li>
34123         * </ul>
34124         * @param {Object} dropEvent
34125         */
34126        "nodedrop" : true,
34127         /**
34128         * @event nodedragover
34129         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34130         * passed to handlers has the following properties:<br />
34131         * <ul style="padding:5px;padding-left:16px;">
34132         * <li>tree - The TreePanel</li>
34133         * <li>target - The node being targeted for the drop</li>
34134         * <li>data - The drag data from the drag source</li>
34135         * <li>point - The point of the drop - append, above or below</li>
34136         * <li>source - The drag source</li>
34137         * <li>rawEvent - Raw mouse event</li>
34138         * <li>dropNode - Drop node(s) provided by the source.</li>
34139         * <li>cancel - Set this to true to signal drop not allowed.</li>
34140         * </ul>
34141         * @param {Object} dragOverEvent
34142         */
34143        "nodedragover" : true
34144         
34145     });
34146     if(this.singleExpand){
34147        this.on("beforeexpand", this.restrictExpand, this);
34148     }
34149     if (this.editor) {
34150         this.editor.tree = this;
34151         this.editor = Roo.factory(this.editor, Roo.tree);
34152     }
34153     
34154     if (this.selModel) {
34155         this.selModel = Roo.factory(this.selModel, Roo.tree);
34156     }
34157    
34158 };
34159 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34160     rootVisible : true,
34161     animate: Roo.enableFx,
34162     lines : true,
34163     enableDD : false,
34164     hlDrop : Roo.enableFx,
34165   
34166     renderer: false,
34167     
34168     rendererTip: false,
34169     // private
34170     restrictExpand : function(node){
34171         var p = node.parentNode;
34172         if(p){
34173             if(p.expandedChild && p.expandedChild.parentNode == p){
34174                 p.expandedChild.collapse();
34175             }
34176             p.expandedChild = node;
34177         }
34178     },
34179
34180     // private override
34181     setRootNode : function(node){
34182         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34183         if(!this.rootVisible){
34184             node.ui = new Roo.tree.RootTreeNodeUI(node);
34185         }
34186         return node;
34187     },
34188
34189     /**
34190      * Returns the container element for this TreePanel
34191      */
34192     getEl : function(){
34193         return this.el;
34194     },
34195
34196     /**
34197      * Returns the default TreeLoader for this TreePanel
34198      */
34199     getLoader : function(){
34200         return this.loader;
34201     },
34202
34203     /**
34204      * Expand all nodes
34205      */
34206     expandAll : function(){
34207         this.root.expand(true);
34208     },
34209
34210     /**
34211      * Collapse all nodes
34212      */
34213     collapseAll : function(){
34214         this.root.collapse(true);
34215     },
34216
34217     /**
34218      * Returns the selection model used by this TreePanel
34219      */
34220     getSelectionModel : function(){
34221         if(!this.selModel){
34222             this.selModel = new Roo.tree.DefaultSelectionModel();
34223         }
34224         return this.selModel;
34225     },
34226
34227     /**
34228      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34229      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34230      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34231      * @return {Array}
34232      */
34233     getChecked : function(a, startNode){
34234         startNode = startNode || this.root;
34235         var r = [];
34236         var f = function(){
34237             if(this.attributes.checked){
34238                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34239             }
34240         }
34241         startNode.cascade(f);
34242         return r;
34243     },
34244
34245     /**
34246      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34247      * @param {String} path
34248      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34249      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34250      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34251      */
34252     expandPath : function(path, attr, callback){
34253         attr = attr || "id";
34254         var keys = path.split(this.pathSeparator);
34255         var curNode = this.root;
34256         if(curNode.attributes[attr] != keys[1]){ // invalid root
34257             if(callback){
34258                 callback(false, null);
34259             }
34260             return;
34261         }
34262         var index = 1;
34263         var f = function(){
34264             if(++index == keys.length){
34265                 if(callback){
34266                     callback(true, curNode);
34267                 }
34268                 return;
34269             }
34270             var c = curNode.findChild(attr, keys[index]);
34271             if(!c){
34272                 if(callback){
34273                     callback(false, curNode);
34274                 }
34275                 return;
34276             }
34277             curNode = c;
34278             c.expand(false, false, f);
34279         };
34280         curNode.expand(false, false, f);
34281     },
34282
34283     /**
34284      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34285      * @param {String} path
34286      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34287      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34288      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34289      */
34290     selectPath : function(path, attr, callback){
34291         attr = attr || "id";
34292         var keys = path.split(this.pathSeparator);
34293         var v = keys.pop();
34294         if(keys.length > 0){
34295             var f = function(success, node){
34296                 if(success && node){
34297                     var n = node.findChild(attr, v);
34298                     if(n){
34299                         n.select();
34300                         if(callback){
34301                             callback(true, n);
34302                         }
34303                     }else if(callback){
34304                         callback(false, n);
34305                     }
34306                 }else{
34307                     if(callback){
34308                         callback(false, n);
34309                     }
34310                 }
34311             };
34312             this.expandPath(keys.join(this.pathSeparator), attr, f);
34313         }else{
34314             this.root.select();
34315             if(callback){
34316                 callback(true, this.root);
34317             }
34318         }
34319     },
34320
34321     getTreeEl : function(){
34322         return this.el;
34323     },
34324
34325     /**
34326      * Trigger rendering of this TreePanel
34327      */
34328     render : function(){
34329         if (this.innerCt) {
34330             return this; // stop it rendering more than once!!
34331         }
34332         
34333         this.innerCt = this.el.createChild({tag:"ul",
34334                cls:"x-tree-root-ct " +
34335                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34336
34337         if(this.containerScroll){
34338             Roo.dd.ScrollManager.register(this.el);
34339         }
34340         if((this.enableDD || this.enableDrop) && !this.dropZone){
34341            /**
34342             * The dropZone used by this tree if drop is enabled
34343             * @type Roo.tree.TreeDropZone
34344             */
34345              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34346                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34347            });
34348         }
34349         if((this.enableDD || this.enableDrag) && !this.dragZone){
34350            /**
34351             * The dragZone used by this tree if drag is enabled
34352             * @type Roo.tree.TreeDragZone
34353             */
34354             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34355                ddGroup: this.ddGroup || "TreeDD",
34356                scroll: this.ddScroll
34357            });
34358         }
34359         this.getSelectionModel().init(this);
34360         if (!this.root) {
34361             Roo.log("ROOT not set in tree");
34362             return this;
34363         }
34364         this.root.render();
34365         if(!this.rootVisible){
34366             this.root.renderChildren();
34367         }
34368         return this;
34369     }
34370 });/*
34371  * Based on:
34372  * Ext JS Library 1.1.1
34373  * Copyright(c) 2006-2007, Ext JS, LLC.
34374  *
34375  * Originally Released Under LGPL - original licence link has changed is not relivant.
34376  *
34377  * Fork - LGPL
34378  * <script type="text/javascript">
34379  */
34380  
34381
34382 /**
34383  * @class Roo.tree.DefaultSelectionModel
34384  * @extends Roo.util.Observable
34385  * The default single selection for a TreePanel.
34386  * @param {Object} cfg Configuration
34387  */
34388 Roo.tree.DefaultSelectionModel = function(cfg){
34389    this.selNode = null;
34390    
34391    
34392    
34393    this.addEvents({
34394        /**
34395         * @event selectionchange
34396         * Fires when the selected node changes
34397         * @param {DefaultSelectionModel} this
34398         * @param {TreeNode} node the new selection
34399         */
34400        "selectionchange" : true,
34401
34402        /**
34403         * @event beforeselect
34404         * Fires before the selected node changes, return false to cancel the change
34405         * @param {DefaultSelectionModel} this
34406         * @param {TreeNode} node the new selection
34407         * @param {TreeNode} node the old selection
34408         */
34409        "beforeselect" : true
34410    });
34411    
34412     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34413 };
34414
34415 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34416     init : function(tree){
34417         this.tree = tree;
34418         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34419         tree.on("click", this.onNodeClick, this);
34420     },
34421     
34422     onNodeClick : function(node, e){
34423         if (e.ctrlKey && this.selNode == node)  {
34424             this.unselect(node);
34425             return;
34426         }
34427         this.select(node);
34428     },
34429     
34430     /**
34431      * Select a node.
34432      * @param {TreeNode} node The node to select
34433      * @return {TreeNode} The selected node
34434      */
34435     select : function(node){
34436         var last = this.selNode;
34437         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34438             if(last){
34439                 last.ui.onSelectedChange(false);
34440             }
34441             this.selNode = node;
34442             node.ui.onSelectedChange(true);
34443             this.fireEvent("selectionchange", this, node, last);
34444         }
34445         return node;
34446     },
34447     
34448     /**
34449      * Deselect a node.
34450      * @param {TreeNode} node The node to unselect
34451      */
34452     unselect : function(node){
34453         if(this.selNode == node){
34454             this.clearSelections();
34455         }    
34456     },
34457     
34458     /**
34459      * Clear all selections
34460      */
34461     clearSelections : function(){
34462         var n = this.selNode;
34463         if(n){
34464             n.ui.onSelectedChange(false);
34465             this.selNode = null;
34466             this.fireEvent("selectionchange", this, null);
34467         }
34468         return n;
34469     },
34470     
34471     /**
34472      * Get the selected node
34473      * @return {TreeNode} The selected node
34474      */
34475     getSelectedNode : function(){
34476         return this.selNode;    
34477     },
34478     
34479     /**
34480      * Returns true if the node is selected
34481      * @param {TreeNode} node The node to check
34482      * @return {Boolean}
34483      */
34484     isSelected : function(node){
34485         return this.selNode == node;  
34486     },
34487
34488     /**
34489      * Selects the node above the selected node in the tree, intelligently walking the nodes
34490      * @return TreeNode The new selection
34491      */
34492     selectPrevious : function(){
34493         var s = this.selNode || this.lastSelNode;
34494         if(!s){
34495             return null;
34496         }
34497         var ps = s.previousSibling;
34498         if(ps){
34499             if(!ps.isExpanded() || ps.childNodes.length < 1){
34500                 return this.select(ps);
34501             } else{
34502                 var lc = ps.lastChild;
34503                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34504                     lc = lc.lastChild;
34505                 }
34506                 return this.select(lc);
34507             }
34508         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34509             return this.select(s.parentNode);
34510         }
34511         return null;
34512     },
34513
34514     /**
34515      * Selects the node above the selected node in the tree, intelligently walking the nodes
34516      * @return TreeNode The new selection
34517      */
34518     selectNext : function(){
34519         var s = this.selNode || this.lastSelNode;
34520         if(!s){
34521             return null;
34522         }
34523         if(s.firstChild && s.isExpanded()){
34524              return this.select(s.firstChild);
34525          }else if(s.nextSibling){
34526              return this.select(s.nextSibling);
34527          }else if(s.parentNode){
34528             var newS = null;
34529             s.parentNode.bubble(function(){
34530                 if(this.nextSibling){
34531                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34532                     return false;
34533                 }
34534             });
34535             return newS;
34536          }
34537         return null;
34538     },
34539
34540     onKeyDown : function(e){
34541         var s = this.selNode || this.lastSelNode;
34542         // undesirable, but required
34543         var sm = this;
34544         if(!s){
34545             return;
34546         }
34547         var k = e.getKey();
34548         switch(k){
34549              case e.DOWN:
34550                  e.stopEvent();
34551                  this.selectNext();
34552              break;
34553              case e.UP:
34554                  e.stopEvent();
34555                  this.selectPrevious();
34556              break;
34557              case e.RIGHT:
34558                  e.preventDefault();
34559                  if(s.hasChildNodes()){
34560                      if(!s.isExpanded()){
34561                          s.expand();
34562                      }else if(s.firstChild){
34563                          this.select(s.firstChild, e);
34564                      }
34565                  }
34566              break;
34567              case e.LEFT:
34568                  e.preventDefault();
34569                  if(s.hasChildNodes() && s.isExpanded()){
34570                      s.collapse();
34571                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34572                      this.select(s.parentNode, e);
34573                  }
34574              break;
34575         };
34576     }
34577 });
34578
34579 /**
34580  * @class Roo.tree.MultiSelectionModel
34581  * @extends Roo.util.Observable
34582  * Multi selection for a TreePanel.
34583  * @param {Object} cfg Configuration
34584  */
34585 Roo.tree.MultiSelectionModel = function(){
34586    this.selNodes = [];
34587    this.selMap = {};
34588    this.addEvents({
34589        /**
34590         * @event selectionchange
34591         * Fires when the selected nodes change
34592         * @param {MultiSelectionModel} this
34593         * @param {Array} nodes Array of the selected nodes
34594         */
34595        "selectionchange" : true
34596    });
34597    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34598    
34599 };
34600
34601 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34602     init : function(tree){
34603         this.tree = tree;
34604         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34605         tree.on("click", this.onNodeClick, this);
34606     },
34607     
34608     onNodeClick : function(node, e){
34609         this.select(node, e, e.ctrlKey);
34610     },
34611     
34612     /**
34613      * Select a node.
34614      * @param {TreeNode} node The node to select
34615      * @param {EventObject} e (optional) An event associated with the selection
34616      * @param {Boolean} keepExisting True to retain existing selections
34617      * @return {TreeNode} The selected node
34618      */
34619     select : function(node, e, keepExisting){
34620         if(keepExisting !== true){
34621             this.clearSelections(true);
34622         }
34623         if(this.isSelected(node)){
34624             this.lastSelNode = node;
34625             return node;
34626         }
34627         this.selNodes.push(node);
34628         this.selMap[node.id] = node;
34629         this.lastSelNode = node;
34630         node.ui.onSelectedChange(true);
34631         this.fireEvent("selectionchange", this, this.selNodes);
34632         return node;
34633     },
34634     
34635     /**
34636      * Deselect a node.
34637      * @param {TreeNode} node The node to unselect
34638      */
34639     unselect : function(node){
34640         if(this.selMap[node.id]){
34641             node.ui.onSelectedChange(false);
34642             var sn = this.selNodes;
34643             var index = -1;
34644             if(sn.indexOf){
34645                 index = sn.indexOf(node);
34646             }else{
34647                 for(var i = 0, len = sn.length; i < len; i++){
34648                     if(sn[i] == node){
34649                         index = i;
34650                         break;
34651                     }
34652                 }
34653             }
34654             if(index != -1){
34655                 this.selNodes.splice(index, 1);
34656             }
34657             delete this.selMap[node.id];
34658             this.fireEvent("selectionchange", this, this.selNodes);
34659         }
34660     },
34661     
34662     /**
34663      * Clear all selections
34664      */
34665     clearSelections : function(suppressEvent){
34666         var sn = this.selNodes;
34667         if(sn.length > 0){
34668             for(var i = 0, len = sn.length; i < len; i++){
34669                 sn[i].ui.onSelectedChange(false);
34670             }
34671             this.selNodes = [];
34672             this.selMap = {};
34673             if(suppressEvent !== true){
34674                 this.fireEvent("selectionchange", this, this.selNodes);
34675             }
34676         }
34677     },
34678     
34679     /**
34680      * Returns true if the node is selected
34681      * @param {TreeNode} node The node to check
34682      * @return {Boolean}
34683      */
34684     isSelected : function(node){
34685         return this.selMap[node.id] ? true : false;  
34686     },
34687     
34688     /**
34689      * Returns an array of the selected nodes
34690      * @return {Array}
34691      */
34692     getSelectedNodes : function(){
34693         return this.selNodes;    
34694     },
34695
34696     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34697
34698     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34699
34700     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34701 });/*
34702  * Based on:
34703  * Ext JS Library 1.1.1
34704  * Copyright(c) 2006-2007, Ext JS, LLC.
34705  *
34706  * Originally Released Under LGPL - original licence link has changed is not relivant.
34707  *
34708  * Fork - LGPL
34709  * <script type="text/javascript">
34710  */
34711  
34712 /**
34713  * @class Roo.tree.TreeNode
34714  * @extends Roo.data.Node
34715  * @cfg {String} text The text for this node
34716  * @cfg {Boolean} expanded true to start the node expanded
34717  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34718  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34719  * @cfg {Boolean} disabled true to start the node disabled
34720  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34721  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
34722  * @cfg {String} cls A css class to be added to the node
34723  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34724  * @cfg {String} href URL of the link used for the node (defaults to #)
34725  * @cfg {String} hrefTarget target frame for the link
34726  * @cfg {String} qtip An Ext QuickTip for the node
34727  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34728  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34729  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34730  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34731  * (defaults to undefined with no checkbox rendered)
34732  * @constructor
34733  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34734  */
34735 Roo.tree.TreeNode = function(attributes){
34736     attributes = attributes || {};
34737     if(typeof attributes == "string"){
34738         attributes = {text: attributes};
34739     }
34740     this.childrenRendered = false;
34741     this.rendered = false;
34742     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34743     this.expanded = attributes.expanded === true;
34744     this.isTarget = attributes.isTarget !== false;
34745     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34746     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34747
34748     /**
34749      * Read-only. The text for this node. To change it use setText().
34750      * @type String
34751      */
34752     this.text = attributes.text;
34753     /**
34754      * True if this node is disabled.
34755      * @type Boolean
34756      */
34757     this.disabled = attributes.disabled === true;
34758
34759     this.addEvents({
34760         /**
34761         * @event textchange
34762         * Fires when the text for this node is changed
34763         * @param {Node} this This node
34764         * @param {String} text The new text
34765         * @param {String} oldText The old text
34766         */
34767         "textchange" : true,
34768         /**
34769         * @event beforeexpand
34770         * Fires before this node is expanded, return false to cancel.
34771         * @param {Node} this This node
34772         * @param {Boolean} deep
34773         * @param {Boolean} anim
34774         */
34775         "beforeexpand" : true,
34776         /**
34777         * @event beforecollapse
34778         * Fires before this node is collapsed, return false to cancel.
34779         * @param {Node} this This node
34780         * @param {Boolean} deep
34781         * @param {Boolean} anim
34782         */
34783         "beforecollapse" : true,
34784         /**
34785         * @event expand
34786         * Fires when this node is expanded
34787         * @param {Node} this This node
34788         */
34789         "expand" : true,
34790         /**
34791         * @event disabledchange
34792         * Fires when the disabled status of this node changes
34793         * @param {Node} this This node
34794         * @param {Boolean} disabled
34795         */
34796         "disabledchange" : true,
34797         /**
34798         * @event collapse
34799         * Fires when this node is collapsed
34800         * @param {Node} this This node
34801         */
34802         "collapse" : true,
34803         /**
34804         * @event beforeclick
34805         * Fires before click processing. Return false to cancel the default action.
34806         * @param {Node} this This node
34807         * @param {Roo.EventObject} e The event object
34808         */
34809         "beforeclick":true,
34810         /**
34811         * @event checkchange
34812         * Fires when a node with a checkbox's checked property changes
34813         * @param {Node} this This node
34814         * @param {Boolean} checked
34815         */
34816         "checkchange":true,
34817         /**
34818         * @event click
34819         * Fires when this node is clicked
34820         * @param {Node} this This node
34821         * @param {Roo.EventObject} e The event object
34822         */
34823         "click":true,
34824         /**
34825         * @event dblclick
34826         * Fires when this node is double clicked
34827         * @param {Node} this This node
34828         * @param {Roo.EventObject} e The event object
34829         */
34830         "dblclick":true,
34831         /**
34832         * @event contextmenu
34833         * Fires when this node is right clicked
34834         * @param {Node} this This node
34835         * @param {Roo.EventObject} e The event object
34836         */
34837         "contextmenu":true,
34838         /**
34839         * @event beforechildrenrendered
34840         * Fires right before the child nodes for this node are rendered
34841         * @param {Node} this This node
34842         */
34843         "beforechildrenrendered":true
34844     });
34845
34846     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
34847
34848     /**
34849      * Read-only. The UI for this node
34850      * @type TreeNodeUI
34851      */
34852     this.ui = new uiClass(this);
34853     
34854     // finally support items[]
34855     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
34856         return;
34857     }
34858     
34859     
34860     Roo.each(this.attributes.items, function(c) {
34861         this.appendChild(Roo.factory(c,Roo.Tree));
34862     }, this);
34863     delete this.attributes.items;
34864     
34865     
34866     
34867 };
34868 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
34869     preventHScroll: true,
34870     /**
34871      * Returns true if this node is expanded
34872      * @return {Boolean}
34873      */
34874     isExpanded : function(){
34875         return this.expanded;
34876     },
34877
34878     /**
34879      * Returns the UI object for this node
34880      * @return {TreeNodeUI}
34881      */
34882     getUI : function(){
34883         return this.ui;
34884     },
34885
34886     // private override
34887     setFirstChild : function(node){
34888         var of = this.firstChild;
34889         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
34890         if(this.childrenRendered && of && node != of){
34891             of.renderIndent(true, true);
34892         }
34893         if(this.rendered){
34894             this.renderIndent(true, true);
34895         }
34896     },
34897
34898     // private override
34899     setLastChild : function(node){
34900         var ol = this.lastChild;
34901         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
34902         if(this.childrenRendered && ol && node != ol){
34903             ol.renderIndent(true, true);
34904         }
34905         if(this.rendered){
34906             this.renderIndent(true, true);
34907         }
34908     },
34909
34910     // these methods are overridden to provide lazy rendering support
34911     // private override
34912     appendChild : function()
34913     {
34914         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
34915         if(node && this.childrenRendered){
34916             node.render();
34917         }
34918         this.ui.updateExpandIcon();
34919         return node;
34920     },
34921
34922     // private override
34923     removeChild : function(node){
34924         this.ownerTree.getSelectionModel().unselect(node);
34925         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
34926         // if it's been rendered remove dom node
34927         if(this.childrenRendered){
34928             node.ui.remove();
34929         }
34930         if(this.childNodes.length < 1){
34931             this.collapse(false, false);
34932         }else{
34933             this.ui.updateExpandIcon();
34934         }
34935         if(!this.firstChild) {
34936             this.childrenRendered = false;
34937         }
34938         return node;
34939     },
34940
34941     // private override
34942     insertBefore : function(node, refNode){
34943         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
34944         if(newNode && refNode && this.childrenRendered){
34945             node.render();
34946         }
34947         this.ui.updateExpandIcon();
34948         return newNode;
34949     },
34950
34951     /**
34952      * Sets the text for this node
34953      * @param {String} text
34954      */
34955     setText : function(text){
34956         var oldText = this.text;
34957         this.text = text;
34958         this.attributes.text = text;
34959         if(this.rendered){ // event without subscribing
34960             this.ui.onTextChange(this, text, oldText);
34961         }
34962         this.fireEvent("textchange", this, text, oldText);
34963     },
34964
34965     /**
34966      * Triggers selection of this node
34967      */
34968     select : function(){
34969         this.getOwnerTree().getSelectionModel().select(this);
34970     },
34971
34972     /**
34973      * Triggers deselection of this node
34974      */
34975     unselect : function(){
34976         this.getOwnerTree().getSelectionModel().unselect(this);
34977     },
34978
34979     /**
34980      * Returns true if this node is selected
34981      * @return {Boolean}
34982      */
34983     isSelected : function(){
34984         return this.getOwnerTree().getSelectionModel().isSelected(this);
34985     },
34986
34987     /**
34988      * Expand this node.
34989      * @param {Boolean} deep (optional) True to expand all children as well
34990      * @param {Boolean} anim (optional) false to cancel the default animation
34991      * @param {Function} callback (optional) A callback to be called when
34992      * expanding this node completes (does not wait for deep expand to complete).
34993      * Called with 1 parameter, this node.
34994      */
34995     expand : function(deep, anim, callback){
34996         if(!this.expanded){
34997             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
34998                 return;
34999             }
35000             if(!this.childrenRendered){
35001                 this.renderChildren();
35002             }
35003             this.expanded = true;
35004             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
35005                 this.ui.animExpand(function(){
35006                     this.fireEvent("expand", this);
35007                     if(typeof callback == "function"){
35008                         callback(this);
35009                     }
35010                     if(deep === true){
35011                         this.expandChildNodes(true);
35012                     }
35013                 }.createDelegate(this));
35014                 return;
35015             }else{
35016                 this.ui.expand();
35017                 this.fireEvent("expand", this);
35018                 if(typeof callback == "function"){
35019                     callback(this);
35020                 }
35021             }
35022         }else{
35023            if(typeof callback == "function"){
35024                callback(this);
35025            }
35026         }
35027         if(deep === true){
35028             this.expandChildNodes(true);
35029         }
35030     },
35031
35032     isHiddenRoot : function(){
35033         return this.isRoot && !this.getOwnerTree().rootVisible;
35034     },
35035
35036     /**
35037      * Collapse this node.
35038      * @param {Boolean} deep (optional) True to collapse all children as well
35039      * @param {Boolean} anim (optional) false to cancel the default animation
35040      */
35041     collapse : function(deep, anim){
35042         if(this.expanded && !this.isHiddenRoot()){
35043             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35044                 return;
35045             }
35046             this.expanded = false;
35047             if((this.getOwnerTree().animate && anim !== false) || anim){
35048                 this.ui.animCollapse(function(){
35049                     this.fireEvent("collapse", this);
35050                     if(deep === true){
35051                         this.collapseChildNodes(true);
35052                     }
35053                 }.createDelegate(this));
35054                 return;
35055             }else{
35056                 this.ui.collapse();
35057                 this.fireEvent("collapse", this);
35058             }
35059         }
35060         if(deep === true){
35061             var cs = this.childNodes;
35062             for(var i = 0, len = cs.length; i < len; i++) {
35063                 cs[i].collapse(true, false);
35064             }
35065         }
35066     },
35067
35068     // private
35069     delayedExpand : function(delay){
35070         if(!this.expandProcId){
35071             this.expandProcId = this.expand.defer(delay, this);
35072         }
35073     },
35074
35075     // private
35076     cancelExpand : function(){
35077         if(this.expandProcId){
35078             clearTimeout(this.expandProcId);
35079         }
35080         this.expandProcId = false;
35081     },
35082
35083     /**
35084      * Toggles expanded/collapsed state of the node
35085      */
35086     toggle : function(){
35087         if(this.expanded){
35088             this.collapse();
35089         }else{
35090             this.expand();
35091         }
35092     },
35093
35094     /**
35095      * Ensures all parent nodes are expanded
35096      */
35097     ensureVisible : function(callback){
35098         var tree = this.getOwnerTree();
35099         tree.expandPath(this.parentNode.getPath(), false, function(){
35100             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35101             Roo.callback(callback);
35102         }.createDelegate(this));
35103     },
35104
35105     /**
35106      * Expand all child nodes
35107      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35108      */
35109     expandChildNodes : function(deep){
35110         var cs = this.childNodes;
35111         for(var i = 0, len = cs.length; i < len; i++) {
35112                 cs[i].expand(deep);
35113         }
35114     },
35115
35116     /**
35117      * Collapse all child nodes
35118      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35119      */
35120     collapseChildNodes : function(deep){
35121         var cs = this.childNodes;
35122         for(var i = 0, len = cs.length; i < len; i++) {
35123                 cs[i].collapse(deep);
35124         }
35125     },
35126
35127     /**
35128      * Disables this node
35129      */
35130     disable : function(){
35131         this.disabled = true;
35132         this.unselect();
35133         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35134             this.ui.onDisableChange(this, true);
35135         }
35136         this.fireEvent("disabledchange", this, true);
35137     },
35138
35139     /**
35140      * Enables this node
35141      */
35142     enable : function(){
35143         this.disabled = false;
35144         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35145             this.ui.onDisableChange(this, false);
35146         }
35147         this.fireEvent("disabledchange", this, false);
35148     },
35149
35150     // private
35151     renderChildren : function(suppressEvent){
35152         if(suppressEvent !== false){
35153             this.fireEvent("beforechildrenrendered", this);
35154         }
35155         var cs = this.childNodes;
35156         for(var i = 0, len = cs.length; i < len; i++){
35157             cs[i].render(true);
35158         }
35159         this.childrenRendered = true;
35160     },
35161
35162     // private
35163     sort : function(fn, scope){
35164         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35165         if(this.childrenRendered){
35166             var cs = this.childNodes;
35167             for(var i = 0, len = cs.length; i < len; i++){
35168                 cs[i].render(true);
35169             }
35170         }
35171     },
35172
35173     // private
35174     render : function(bulkRender){
35175         this.ui.render(bulkRender);
35176         if(!this.rendered){
35177             this.rendered = true;
35178             if(this.expanded){
35179                 this.expanded = false;
35180                 this.expand(false, false);
35181             }
35182         }
35183     },
35184
35185     // private
35186     renderIndent : function(deep, refresh){
35187         if(refresh){
35188             this.ui.childIndent = null;
35189         }
35190         this.ui.renderIndent();
35191         if(deep === true && this.childrenRendered){
35192             var cs = this.childNodes;
35193             for(var i = 0, len = cs.length; i < len; i++){
35194                 cs[i].renderIndent(true, refresh);
35195             }
35196         }
35197     }
35198 });/*
35199  * Based on:
35200  * Ext JS Library 1.1.1
35201  * Copyright(c) 2006-2007, Ext JS, LLC.
35202  *
35203  * Originally Released Under LGPL - original licence link has changed is not relivant.
35204  *
35205  * Fork - LGPL
35206  * <script type="text/javascript">
35207  */
35208  
35209 /**
35210  * @class Roo.tree.AsyncTreeNode
35211  * @extends Roo.tree.TreeNode
35212  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35213  * @constructor
35214  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35215  */
35216  Roo.tree.AsyncTreeNode = function(config){
35217     this.loaded = false;
35218     this.loading = false;
35219     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35220     /**
35221     * @event beforeload
35222     * Fires before this node is loaded, return false to cancel
35223     * @param {Node} this This node
35224     */
35225     this.addEvents({'beforeload':true, 'load': true});
35226     /**
35227     * @event load
35228     * Fires when this node is loaded
35229     * @param {Node} this This node
35230     */
35231     /**
35232      * The loader used by this node (defaults to using the tree's defined loader)
35233      * @type TreeLoader
35234      * @property loader
35235      */
35236 };
35237 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35238     expand : function(deep, anim, callback){
35239         if(this.loading){ // if an async load is already running, waiting til it's done
35240             var timer;
35241             var f = function(){
35242                 if(!this.loading){ // done loading
35243                     clearInterval(timer);
35244                     this.expand(deep, anim, callback);
35245                 }
35246             }.createDelegate(this);
35247             timer = setInterval(f, 200);
35248             return;
35249         }
35250         if(!this.loaded){
35251             if(this.fireEvent("beforeload", this) === false){
35252                 return;
35253             }
35254             this.loading = true;
35255             this.ui.beforeLoad(this);
35256             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35257             if(loader){
35258                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35259                 return;
35260             }
35261         }
35262         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35263     },
35264     
35265     /**
35266      * Returns true if this node is currently loading
35267      * @return {Boolean}
35268      */
35269     isLoading : function(){
35270         return this.loading;  
35271     },
35272     
35273     loadComplete : function(deep, anim, callback){
35274         this.loading = false;
35275         this.loaded = true;
35276         this.ui.afterLoad(this);
35277         this.fireEvent("load", this);
35278         this.expand(deep, anim, callback);
35279     },
35280     
35281     /**
35282      * Returns true if this node has been loaded
35283      * @return {Boolean}
35284      */
35285     isLoaded : function(){
35286         return this.loaded;
35287     },
35288     
35289     hasChildNodes : function(){
35290         if(!this.isLeaf() && !this.loaded){
35291             return true;
35292         }else{
35293             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35294         }
35295     },
35296
35297     /**
35298      * Trigger a reload for this node
35299      * @param {Function} callback
35300      */
35301     reload : function(callback){
35302         this.collapse(false, false);
35303         while(this.firstChild){
35304             this.removeChild(this.firstChild);
35305         }
35306         this.childrenRendered = false;
35307         this.loaded = false;
35308         if(this.isHiddenRoot()){
35309             this.expanded = false;
35310         }
35311         this.expand(false, false, callback);
35312     }
35313 });/*
35314  * Based on:
35315  * Ext JS Library 1.1.1
35316  * Copyright(c) 2006-2007, Ext JS, LLC.
35317  *
35318  * Originally Released Under LGPL - original licence link has changed is not relivant.
35319  *
35320  * Fork - LGPL
35321  * <script type="text/javascript">
35322  */
35323  
35324 /**
35325  * @class Roo.tree.TreeNodeUI
35326  * @constructor
35327  * @param {Object} node The node to render
35328  * The TreeNode UI implementation is separate from the
35329  * tree implementation. Unless you are customizing the tree UI,
35330  * you should never have to use this directly.
35331  */
35332 Roo.tree.TreeNodeUI = function(node){
35333     this.node = node;
35334     this.rendered = false;
35335     this.animating = false;
35336     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35337 };
35338
35339 Roo.tree.TreeNodeUI.prototype = {
35340     removeChild : function(node){
35341         if(this.rendered){
35342             this.ctNode.removeChild(node.ui.getEl());
35343         }
35344     },
35345
35346     beforeLoad : function(){
35347          this.addClass("x-tree-node-loading");
35348     },
35349
35350     afterLoad : function(){
35351          this.removeClass("x-tree-node-loading");
35352     },
35353
35354     onTextChange : function(node, text, oldText){
35355         if(this.rendered){
35356             this.textNode.innerHTML = text;
35357         }
35358     },
35359
35360     onDisableChange : function(node, state){
35361         this.disabled = state;
35362         if(state){
35363             this.addClass("x-tree-node-disabled");
35364         }else{
35365             this.removeClass("x-tree-node-disabled");
35366         }
35367     },
35368
35369     onSelectedChange : function(state){
35370         if(state){
35371             this.focus();
35372             this.addClass("x-tree-selected");
35373         }else{
35374             //this.blur();
35375             this.removeClass("x-tree-selected");
35376         }
35377     },
35378
35379     onMove : function(tree, node, oldParent, newParent, index, refNode){
35380         this.childIndent = null;
35381         if(this.rendered){
35382             var targetNode = newParent.ui.getContainer();
35383             if(!targetNode){//target not rendered
35384                 this.holder = document.createElement("div");
35385                 this.holder.appendChild(this.wrap);
35386                 return;
35387             }
35388             var insertBefore = refNode ? refNode.ui.getEl() : null;
35389             if(insertBefore){
35390                 targetNode.insertBefore(this.wrap, insertBefore);
35391             }else{
35392                 targetNode.appendChild(this.wrap);
35393             }
35394             this.node.renderIndent(true);
35395         }
35396     },
35397
35398     addClass : function(cls){
35399         if(this.elNode){
35400             Roo.fly(this.elNode).addClass(cls);
35401         }
35402     },
35403
35404     removeClass : function(cls){
35405         if(this.elNode){
35406             Roo.fly(this.elNode).removeClass(cls);
35407         }
35408     },
35409
35410     remove : function(){
35411         if(this.rendered){
35412             this.holder = document.createElement("div");
35413             this.holder.appendChild(this.wrap);
35414         }
35415     },
35416
35417     fireEvent : function(){
35418         return this.node.fireEvent.apply(this.node, arguments);
35419     },
35420
35421     initEvents : function(){
35422         this.node.on("move", this.onMove, this);
35423         var E = Roo.EventManager;
35424         var a = this.anchor;
35425
35426         var el = Roo.fly(a, '_treeui');
35427
35428         if(Roo.isOpera){ // opera render bug ignores the CSS
35429             el.setStyle("text-decoration", "none");
35430         }
35431
35432         el.on("click", this.onClick, this);
35433         el.on("dblclick", this.onDblClick, this);
35434
35435         if(this.checkbox){
35436             Roo.EventManager.on(this.checkbox,
35437                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35438         }
35439
35440         el.on("contextmenu", this.onContextMenu, this);
35441
35442         var icon = Roo.fly(this.iconNode);
35443         icon.on("click", this.onClick, this);
35444         icon.on("dblclick", this.onDblClick, this);
35445         icon.on("contextmenu", this.onContextMenu, this);
35446         E.on(this.ecNode, "click", this.ecClick, this, true);
35447
35448         if(this.node.disabled){
35449             this.addClass("x-tree-node-disabled");
35450         }
35451         if(this.node.hidden){
35452             this.addClass("x-tree-node-disabled");
35453         }
35454         var ot = this.node.getOwnerTree();
35455         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
35456         if(dd && (!this.node.isRoot || ot.rootVisible)){
35457             Roo.dd.Registry.register(this.elNode, {
35458                 node: this.node,
35459                 handles: this.getDDHandles(),
35460                 isHandle: false
35461             });
35462         }
35463     },
35464
35465     getDDHandles : function(){
35466         return [this.iconNode, this.textNode];
35467     },
35468
35469     hide : function(){
35470         if(this.rendered){
35471             this.wrap.style.display = "none";
35472         }
35473     },
35474
35475     show : function(){
35476         if(this.rendered){
35477             this.wrap.style.display = "";
35478         }
35479     },
35480
35481     onContextMenu : function(e){
35482         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35483             e.preventDefault();
35484             this.focus();
35485             this.fireEvent("contextmenu", this.node, e);
35486         }
35487     },
35488
35489     onClick : function(e){
35490         if(this.dropping){
35491             e.stopEvent();
35492             return;
35493         }
35494         if(this.fireEvent("beforeclick", this.node, e) !== false){
35495             if(!this.disabled && this.node.attributes.href){
35496                 this.fireEvent("click", this.node, e);
35497                 return;
35498             }
35499             e.preventDefault();
35500             if(this.disabled){
35501                 return;
35502             }
35503
35504             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35505                 this.node.toggle();
35506             }
35507
35508             this.fireEvent("click", this.node, e);
35509         }else{
35510             e.stopEvent();
35511         }
35512     },
35513
35514     onDblClick : function(e){
35515         e.preventDefault();
35516         if(this.disabled){
35517             return;
35518         }
35519         if(this.checkbox){
35520             this.toggleCheck();
35521         }
35522         if(!this.animating && this.node.hasChildNodes()){
35523             this.node.toggle();
35524         }
35525         this.fireEvent("dblclick", this.node, e);
35526     },
35527
35528     onCheckChange : function(){
35529         var checked = this.checkbox.checked;
35530         this.node.attributes.checked = checked;
35531         this.fireEvent('checkchange', this.node, checked);
35532     },
35533
35534     ecClick : function(e){
35535         if(!this.animating && this.node.hasChildNodes()){
35536             this.node.toggle();
35537         }
35538     },
35539
35540     startDrop : function(){
35541         this.dropping = true;
35542     },
35543
35544     // delayed drop so the click event doesn't get fired on a drop
35545     endDrop : function(){
35546        setTimeout(function(){
35547            this.dropping = false;
35548        }.createDelegate(this), 50);
35549     },
35550
35551     expand : function(){
35552         this.updateExpandIcon();
35553         this.ctNode.style.display = "";
35554     },
35555
35556     focus : function(){
35557         if(!this.node.preventHScroll){
35558             try{this.anchor.focus();
35559             }catch(e){}
35560         }else if(!Roo.isIE){
35561             try{
35562                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35563                 var l = noscroll.scrollLeft;
35564                 this.anchor.focus();
35565                 noscroll.scrollLeft = l;
35566             }catch(e){}
35567         }
35568     },
35569
35570     toggleCheck : function(value){
35571         var cb = this.checkbox;
35572         if(cb){
35573             cb.checked = (value === undefined ? !cb.checked : value);
35574         }
35575     },
35576
35577     blur : function(){
35578         try{
35579             this.anchor.blur();
35580         }catch(e){}
35581     },
35582
35583     animExpand : function(callback){
35584         var ct = Roo.get(this.ctNode);
35585         ct.stopFx();
35586         if(!this.node.hasChildNodes()){
35587             this.updateExpandIcon();
35588             this.ctNode.style.display = "";
35589             Roo.callback(callback);
35590             return;
35591         }
35592         this.animating = true;
35593         this.updateExpandIcon();
35594
35595         ct.slideIn('t', {
35596            callback : function(){
35597                this.animating = false;
35598                Roo.callback(callback);
35599             },
35600             scope: this,
35601             duration: this.node.ownerTree.duration || .25
35602         });
35603     },
35604
35605     highlight : function(){
35606         var tree = this.node.getOwnerTree();
35607         Roo.fly(this.wrap).highlight(
35608             tree.hlColor || "C3DAF9",
35609             {endColor: tree.hlBaseColor}
35610         );
35611     },
35612
35613     collapse : function(){
35614         this.updateExpandIcon();
35615         this.ctNode.style.display = "none";
35616     },
35617
35618     animCollapse : function(callback){
35619         var ct = Roo.get(this.ctNode);
35620         ct.enableDisplayMode('block');
35621         ct.stopFx();
35622
35623         this.animating = true;
35624         this.updateExpandIcon();
35625
35626         ct.slideOut('t', {
35627             callback : function(){
35628                this.animating = false;
35629                Roo.callback(callback);
35630             },
35631             scope: this,
35632             duration: this.node.ownerTree.duration || .25
35633         });
35634     },
35635
35636     getContainer : function(){
35637         return this.ctNode;
35638     },
35639
35640     getEl : function(){
35641         return this.wrap;
35642     },
35643
35644     appendDDGhost : function(ghostNode){
35645         ghostNode.appendChild(this.elNode.cloneNode(true));
35646     },
35647
35648     getDDRepairXY : function(){
35649         return Roo.lib.Dom.getXY(this.iconNode);
35650     },
35651
35652     onRender : function(){
35653         this.render();
35654     },
35655
35656     render : function(bulkRender){
35657         var n = this.node, a = n.attributes;
35658         var targetNode = n.parentNode ?
35659               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35660
35661         if(!this.rendered){
35662             this.rendered = true;
35663
35664             this.renderElements(n, a, targetNode, bulkRender);
35665
35666             if(a.qtip){
35667                if(this.textNode.setAttributeNS){
35668                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35669                    if(a.qtipTitle){
35670                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35671                    }
35672                }else{
35673                    this.textNode.setAttribute("ext:qtip", a.qtip);
35674                    if(a.qtipTitle){
35675                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35676                    }
35677                }
35678             }else if(a.qtipCfg){
35679                 a.qtipCfg.target = Roo.id(this.textNode);
35680                 Roo.QuickTips.register(a.qtipCfg);
35681             }
35682             this.initEvents();
35683             if(!this.node.expanded){
35684                 this.updateExpandIcon();
35685             }
35686         }else{
35687             if(bulkRender === true) {
35688                 targetNode.appendChild(this.wrap);
35689             }
35690         }
35691     },
35692
35693     renderElements : function(n, a, targetNode, bulkRender)
35694     {
35695         // add some indent caching, this helps performance when rendering a large tree
35696         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35697         var t = n.getOwnerTree();
35698         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35699         if (typeof(n.attributes.html) != 'undefined') {
35700             txt = n.attributes.html;
35701         }
35702         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
35703         var cb = typeof a.checked == 'boolean';
35704         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35705         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35706             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35707             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35708             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35709             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35710             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35711              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35712                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35713             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35714             "</li>"];
35715
35716         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35717             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35718                                 n.nextSibling.ui.getEl(), buf.join(""));
35719         }else{
35720             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35721         }
35722
35723         this.elNode = this.wrap.childNodes[0];
35724         this.ctNode = this.wrap.childNodes[1];
35725         var cs = this.elNode.childNodes;
35726         this.indentNode = cs[0];
35727         this.ecNode = cs[1];
35728         this.iconNode = cs[2];
35729         var index = 3;
35730         if(cb){
35731             this.checkbox = cs[3];
35732             index++;
35733         }
35734         this.anchor = cs[index];
35735         this.textNode = cs[index].firstChild;
35736     },
35737
35738     getAnchor : function(){
35739         return this.anchor;
35740     },
35741
35742     getTextEl : function(){
35743         return this.textNode;
35744     },
35745
35746     getIconEl : function(){
35747         return this.iconNode;
35748     },
35749
35750     isChecked : function(){
35751         return this.checkbox ? this.checkbox.checked : false;
35752     },
35753
35754     updateExpandIcon : function(){
35755         if(this.rendered){
35756             var n = this.node, c1, c2;
35757             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35758             var hasChild = n.hasChildNodes();
35759             if(hasChild){
35760                 if(n.expanded){
35761                     cls += "-minus";
35762                     c1 = "x-tree-node-collapsed";
35763                     c2 = "x-tree-node-expanded";
35764                 }else{
35765                     cls += "-plus";
35766                     c1 = "x-tree-node-expanded";
35767                     c2 = "x-tree-node-collapsed";
35768                 }
35769                 if(this.wasLeaf){
35770                     this.removeClass("x-tree-node-leaf");
35771                     this.wasLeaf = false;
35772                 }
35773                 if(this.c1 != c1 || this.c2 != c2){
35774                     Roo.fly(this.elNode).replaceClass(c1, c2);
35775                     this.c1 = c1; this.c2 = c2;
35776                 }
35777             }else{
35778                 // this changes non-leafs into leafs if they have no children.
35779                 // it's not very rational behaviour..
35780                 
35781                 if(!this.wasLeaf && this.node.leaf){
35782                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35783                     delete this.c1;
35784                     delete this.c2;
35785                     this.wasLeaf = true;
35786                 }
35787             }
35788             var ecc = "x-tree-ec-icon "+cls;
35789             if(this.ecc != ecc){
35790                 this.ecNode.className = ecc;
35791                 this.ecc = ecc;
35792             }
35793         }
35794     },
35795
35796     getChildIndent : function(){
35797         if(!this.childIndent){
35798             var buf = [];
35799             var p = this.node;
35800             while(p){
35801                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35802                     if(!p.isLast()) {
35803                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35804                     } else {
35805                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35806                     }
35807                 }
35808                 p = p.parentNode;
35809             }
35810             this.childIndent = buf.join("");
35811         }
35812         return this.childIndent;
35813     },
35814
35815     renderIndent : function(){
35816         if(this.rendered){
35817             var indent = "";
35818             var p = this.node.parentNode;
35819             if(p){
35820                 indent = p.ui.getChildIndent();
35821             }
35822             if(this.indentMarkup != indent){ // don't rerender if not required
35823                 this.indentNode.innerHTML = indent;
35824                 this.indentMarkup = indent;
35825             }
35826             this.updateExpandIcon();
35827         }
35828     }
35829 };
35830
35831 Roo.tree.RootTreeNodeUI = function(){
35832     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35833 };
35834 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35835     render : function(){
35836         if(!this.rendered){
35837             var targetNode = this.node.ownerTree.innerCt.dom;
35838             this.node.expanded = true;
35839             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
35840             this.wrap = this.ctNode = targetNode.firstChild;
35841         }
35842     },
35843     collapse : function(){
35844     },
35845     expand : function(){
35846     }
35847 });/*
35848  * Based on:
35849  * Ext JS Library 1.1.1
35850  * Copyright(c) 2006-2007, Ext JS, LLC.
35851  *
35852  * Originally Released Under LGPL - original licence link has changed is not relivant.
35853  *
35854  * Fork - LGPL
35855  * <script type="text/javascript">
35856  */
35857 /**
35858  * @class Roo.tree.TreeLoader
35859  * @extends Roo.util.Observable
35860  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
35861  * nodes from a specified URL. The response must be a javascript Array definition
35862  * who's elements are node definition objects. eg:
35863  * <pre><code>
35864 {  success : true,
35865    data :      [
35866    
35867     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
35868     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
35869     ]
35870 }
35871
35872
35873 </code></pre>
35874  * <br><br>
35875  * The old style respose with just an array is still supported, but not recommended.
35876  * <br><br>
35877  *
35878  * A server request is sent, and child nodes are loaded only when a node is expanded.
35879  * The loading node's id is passed to the server under the parameter name "node" to
35880  * enable the server to produce the correct child nodes.
35881  * <br><br>
35882  * To pass extra parameters, an event handler may be attached to the "beforeload"
35883  * event, and the parameters specified in the TreeLoader's baseParams property:
35884  * <pre><code>
35885     myTreeLoader.on("beforeload", function(treeLoader, node) {
35886         this.baseParams.category = node.attributes.category;
35887     }, this);
35888 </code></pre><
35889  * This would pass an HTTP parameter called "category" to the server containing
35890  * the value of the Node's "category" attribute.
35891  * @constructor
35892  * Creates a new Treeloader.
35893  * @param {Object} config A config object containing config properties.
35894  */
35895 Roo.tree.TreeLoader = function(config){
35896     this.baseParams = {};
35897     this.requestMethod = "POST";
35898     Roo.apply(this, config);
35899
35900     this.addEvents({
35901     
35902         /**
35903          * @event beforeload
35904          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
35905          * @param {Object} This TreeLoader object.
35906          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35907          * @param {Object} callback The callback function specified in the {@link #load} call.
35908          */
35909         beforeload : true,
35910         /**
35911          * @event load
35912          * Fires when the node has been successfuly loaded.
35913          * @param {Object} This TreeLoader object.
35914          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35915          * @param {Object} response The response object containing the data from the server.
35916          */
35917         load : true,
35918         /**
35919          * @event loadexception
35920          * Fires if the network request failed.
35921          * @param {Object} This TreeLoader object.
35922          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
35923          * @param {Object} response The response object containing the data from the server.
35924          */
35925         loadexception : true,
35926         /**
35927          * @event create
35928          * Fires before a node is created, enabling you to return custom Node types 
35929          * @param {Object} This TreeLoader object.
35930          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
35931          */
35932         create : true
35933     });
35934
35935     Roo.tree.TreeLoader.superclass.constructor.call(this);
35936 };
35937
35938 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
35939     /**
35940     * @cfg {String} dataUrl The URL from which to request a Json string which
35941     * specifies an array of node definition object representing the child nodes
35942     * to be loaded.
35943     */
35944     /**
35945     * @cfg {String} requestMethod either GET or POST
35946     * defaults to POST (due to BC)
35947     * to be loaded.
35948     */
35949     /**
35950     * @cfg {Object} baseParams (optional) An object containing properties which
35951     * specify HTTP parameters to be passed to each request for child nodes.
35952     */
35953     /**
35954     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
35955     * created by this loader. If the attributes sent by the server have an attribute in this object,
35956     * they take priority.
35957     */
35958     /**
35959     * @cfg {Object} uiProviders (optional) An object containing properties which
35960     * 
35961     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
35962     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
35963     * <i>uiProvider</i> attribute of a returned child node is a string rather
35964     * than a reference to a TreeNodeUI implementation, this that string value
35965     * is used as a property name in the uiProviders object. You can define the provider named
35966     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
35967     */
35968     uiProviders : {},
35969
35970     /**
35971     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
35972     * child nodes before loading.
35973     */
35974     clearOnLoad : true,
35975
35976     /**
35977     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
35978     * property on loading, rather than expecting an array. (eg. more compatible to a standard
35979     * Grid query { data : [ .....] }
35980     */
35981     
35982     root : false,
35983      /**
35984     * @cfg {String} queryParam (optional) 
35985     * Name of the query as it will be passed on the querystring (defaults to 'node')
35986     * eg. the request will be ?node=[id]
35987     */
35988     
35989     
35990     queryParam: false,
35991     
35992     /**
35993      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
35994      * This is called automatically when a node is expanded, but may be used to reload
35995      * a node (or append new children if the {@link #clearOnLoad} option is false.)
35996      * @param {Roo.tree.TreeNode} node
35997      * @param {Function} callback
35998      */
35999     load : function(node, callback){
36000         if(this.clearOnLoad){
36001             while(node.firstChild){
36002                 node.removeChild(node.firstChild);
36003             }
36004         }
36005         if(node.attributes.children){ // preloaded json children
36006             var cs = node.attributes.children;
36007             for(var i = 0, len = cs.length; i < len; i++){
36008                 node.appendChild(this.createNode(cs[i]));
36009             }
36010             if(typeof callback == "function"){
36011                 callback();
36012             }
36013         }else if(this.dataUrl){
36014             this.requestData(node, callback);
36015         }
36016     },
36017
36018     getParams: function(node){
36019         var buf = [], bp = this.baseParams;
36020         for(var key in bp){
36021             if(typeof bp[key] != "function"){
36022                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36023             }
36024         }
36025         var n = this.queryParam === false ? 'node' : this.queryParam;
36026         buf.push(n + "=", encodeURIComponent(node.id));
36027         return buf.join("");
36028     },
36029
36030     requestData : function(node, callback){
36031         if(this.fireEvent("beforeload", this, node, callback) !== false){
36032             this.transId = Roo.Ajax.request({
36033                 method:this.requestMethod,
36034                 url: this.dataUrl||this.url,
36035                 success: this.handleResponse,
36036                 failure: this.handleFailure,
36037                 scope: this,
36038                 argument: {callback: callback, node: node},
36039                 params: this.getParams(node)
36040             });
36041         }else{
36042             // if the load is cancelled, make sure we notify
36043             // the node that we are done
36044             if(typeof callback == "function"){
36045                 callback();
36046             }
36047         }
36048     },
36049
36050     isLoading : function(){
36051         return this.transId ? true : false;
36052     },
36053
36054     abort : function(){
36055         if(this.isLoading()){
36056             Roo.Ajax.abort(this.transId);
36057         }
36058     },
36059
36060     // private
36061     createNode : function(attr)
36062     {
36063         // apply baseAttrs, nice idea Corey!
36064         if(this.baseAttrs){
36065             Roo.applyIf(attr, this.baseAttrs);
36066         }
36067         if(this.applyLoader !== false){
36068             attr.loader = this;
36069         }
36070         // uiProvider = depreciated..
36071         
36072         if(typeof(attr.uiProvider) == 'string'){
36073            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36074                 /**  eval:var:attr */ eval(attr.uiProvider);
36075         }
36076         if(typeof(this.uiProviders['default']) != 'undefined') {
36077             attr.uiProvider = this.uiProviders['default'];
36078         }
36079         
36080         this.fireEvent('create', this, attr);
36081         
36082         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36083         return(attr.leaf ?
36084                         new Roo.tree.TreeNode(attr) :
36085                         new Roo.tree.AsyncTreeNode(attr));
36086     },
36087
36088     processResponse : function(response, node, callback)
36089     {
36090         var json = response.responseText;
36091         try {
36092             
36093             var o = Roo.decode(json);
36094             
36095             if (this.root === false && typeof(o.success) != undefined) {
36096                 this.root = 'data'; // the default behaviour for list like data..
36097                 }
36098                 
36099             if (this.root !== false &&  !o.success) {
36100                 // it's a failure condition.
36101                 var a = response.argument;
36102                 this.fireEvent("loadexception", this, a.node, response);
36103                 Roo.log("Load failed - should have a handler really");
36104                 return;
36105             }
36106             
36107             
36108             
36109             if (this.root !== false) {
36110                  o = o[this.root];
36111             }
36112             
36113             for(var i = 0, len = o.length; i < len; i++){
36114                 var n = this.createNode(o[i]);
36115                 if(n){
36116                     node.appendChild(n);
36117                 }
36118             }
36119             if(typeof callback == "function"){
36120                 callback(this, node);
36121             }
36122         }catch(e){
36123             this.handleFailure(response);
36124         }
36125     },
36126
36127     handleResponse : function(response){
36128         this.transId = false;
36129         var a = response.argument;
36130         this.processResponse(response, a.node, a.callback);
36131         this.fireEvent("load", this, a.node, response);
36132     },
36133
36134     handleFailure : function(response)
36135     {
36136         // should handle failure better..
36137         this.transId = false;
36138         var a = response.argument;
36139         this.fireEvent("loadexception", this, a.node, response);
36140         if(typeof a.callback == "function"){
36141             a.callback(this, a.node);
36142         }
36143     }
36144 });/*
36145  * Based on:
36146  * Ext JS Library 1.1.1
36147  * Copyright(c) 2006-2007, Ext JS, LLC.
36148  *
36149  * Originally Released Under LGPL - original licence link has changed is not relivant.
36150  *
36151  * Fork - LGPL
36152  * <script type="text/javascript">
36153  */
36154
36155 /**
36156 * @class Roo.tree.TreeFilter
36157 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36158 * @param {TreePanel} tree
36159 * @param {Object} config (optional)
36160  */
36161 Roo.tree.TreeFilter = function(tree, config){
36162     this.tree = tree;
36163     this.filtered = {};
36164     Roo.apply(this, config);
36165 };
36166
36167 Roo.tree.TreeFilter.prototype = {
36168     clearBlank:false,
36169     reverse:false,
36170     autoClear:false,
36171     remove:false,
36172
36173      /**
36174      * Filter the data by a specific attribute.
36175      * @param {String/RegExp} value Either string that the attribute value
36176      * should start with or a RegExp to test against the attribute
36177      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36178      * @param {TreeNode} startNode (optional) The node to start the filter at.
36179      */
36180     filter : function(value, attr, startNode){
36181         attr = attr || "text";
36182         var f;
36183         if(typeof value == "string"){
36184             var vlen = value.length;
36185             // auto clear empty filter
36186             if(vlen == 0 && this.clearBlank){
36187                 this.clear();
36188                 return;
36189             }
36190             value = value.toLowerCase();
36191             f = function(n){
36192                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36193             };
36194         }else if(value.exec){ // regex?
36195             f = function(n){
36196                 return value.test(n.attributes[attr]);
36197             };
36198         }else{
36199             throw 'Illegal filter type, must be string or regex';
36200         }
36201         this.filterBy(f, null, startNode);
36202         },
36203
36204     /**
36205      * Filter by a function. The passed function will be called with each
36206      * node in the tree (or from the startNode). If the function returns true, the node is kept
36207      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36208      * @param {Function} fn The filter function
36209      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36210      */
36211     filterBy : function(fn, scope, startNode){
36212         startNode = startNode || this.tree.root;
36213         if(this.autoClear){
36214             this.clear();
36215         }
36216         var af = this.filtered, rv = this.reverse;
36217         var f = function(n){
36218             if(n == startNode){
36219                 return true;
36220             }
36221             if(af[n.id]){
36222                 return false;
36223             }
36224             var m = fn.call(scope || n, n);
36225             if(!m || rv){
36226                 af[n.id] = n;
36227                 n.ui.hide();
36228                 return false;
36229             }
36230             return true;
36231         };
36232         startNode.cascade(f);
36233         if(this.remove){
36234            for(var id in af){
36235                if(typeof id != "function"){
36236                    var n = af[id];
36237                    if(n && n.parentNode){
36238                        n.parentNode.removeChild(n);
36239                    }
36240                }
36241            }
36242         }
36243     },
36244
36245     /**
36246      * Clears the current filter. Note: with the "remove" option
36247      * set a filter cannot be cleared.
36248      */
36249     clear : function(){
36250         var t = this.tree;
36251         var af = this.filtered;
36252         for(var id in af){
36253             if(typeof id != "function"){
36254                 var n = af[id];
36255                 if(n){
36256                     n.ui.show();
36257                 }
36258             }
36259         }
36260         this.filtered = {};
36261     }
36262 };
36263 /*
36264  * Based on:
36265  * Ext JS Library 1.1.1
36266  * Copyright(c) 2006-2007, Ext JS, LLC.
36267  *
36268  * Originally Released Under LGPL - original licence link has changed is not relivant.
36269  *
36270  * Fork - LGPL
36271  * <script type="text/javascript">
36272  */
36273  
36274
36275 /**
36276  * @class Roo.tree.TreeSorter
36277  * Provides sorting of nodes in a TreePanel
36278  * 
36279  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36280  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36281  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36282  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36283  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36284  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36285  * @constructor
36286  * @param {TreePanel} tree
36287  * @param {Object} config
36288  */
36289 Roo.tree.TreeSorter = function(tree, config){
36290     Roo.apply(this, config);
36291     tree.on("beforechildrenrendered", this.doSort, this);
36292     tree.on("append", this.updateSort, this);
36293     tree.on("insert", this.updateSort, this);
36294     
36295     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36296     var p = this.property || "text";
36297     var sortType = this.sortType;
36298     var fs = this.folderSort;
36299     var cs = this.caseSensitive === true;
36300     var leafAttr = this.leafAttr || 'leaf';
36301
36302     this.sortFn = function(n1, n2){
36303         if(fs){
36304             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36305                 return 1;
36306             }
36307             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36308                 return -1;
36309             }
36310         }
36311         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36312         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36313         if(v1 < v2){
36314                         return dsc ? +1 : -1;
36315                 }else if(v1 > v2){
36316                         return dsc ? -1 : +1;
36317         }else{
36318                 return 0;
36319         }
36320     };
36321 };
36322
36323 Roo.tree.TreeSorter.prototype = {
36324     doSort : function(node){
36325         node.sort(this.sortFn);
36326     },
36327     
36328     compareNodes : function(n1, n2){
36329         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36330     },
36331     
36332     updateSort : function(tree, node){
36333         if(node.childrenRendered){
36334             this.doSort.defer(1, this, [node]);
36335         }
36336     }
36337 };/*
36338  * Based on:
36339  * Ext JS Library 1.1.1
36340  * Copyright(c) 2006-2007, Ext JS, LLC.
36341  *
36342  * Originally Released Under LGPL - original licence link has changed is not relivant.
36343  *
36344  * Fork - LGPL
36345  * <script type="text/javascript">
36346  */
36347
36348 if(Roo.dd.DropZone){
36349     
36350 Roo.tree.TreeDropZone = function(tree, config){
36351     this.allowParentInsert = false;
36352     this.allowContainerDrop = false;
36353     this.appendOnly = false;
36354     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36355     this.tree = tree;
36356     this.lastInsertClass = "x-tree-no-status";
36357     this.dragOverData = {};
36358 };
36359
36360 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36361     ddGroup : "TreeDD",
36362     scroll:  true,
36363     
36364     expandDelay : 1000,
36365     
36366     expandNode : function(node){
36367         if(node.hasChildNodes() && !node.isExpanded()){
36368             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36369         }
36370     },
36371     
36372     queueExpand : function(node){
36373         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36374     },
36375     
36376     cancelExpand : function(){
36377         if(this.expandProcId){
36378             clearTimeout(this.expandProcId);
36379             this.expandProcId = false;
36380         }
36381     },
36382     
36383     isValidDropPoint : function(n, pt, dd, e, data){
36384         if(!n || !data){ return false; }
36385         var targetNode = n.node;
36386         var dropNode = data.node;
36387         // default drop rules
36388         if(!(targetNode && targetNode.isTarget && pt)){
36389             return false;
36390         }
36391         if(pt == "append" && targetNode.allowChildren === false){
36392             return false;
36393         }
36394         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36395             return false;
36396         }
36397         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36398             return false;
36399         }
36400         // reuse the object
36401         var overEvent = this.dragOverData;
36402         overEvent.tree = this.tree;
36403         overEvent.target = targetNode;
36404         overEvent.data = data;
36405         overEvent.point = pt;
36406         overEvent.source = dd;
36407         overEvent.rawEvent = e;
36408         overEvent.dropNode = dropNode;
36409         overEvent.cancel = false;  
36410         var result = this.tree.fireEvent("nodedragover", overEvent);
36411         return overEvent.cancel === false && result !== false;
36412     },
36413     
36414     getDropPoint : function(e, n, dd)
36415     {
36416         var tn = n.node;
36417         if(tn.isRoot){
36418             return tn.allowChildren !== false ? "append" : false; // always append for root
36419         }
36420         var dragEl = n.ddel;
36421         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36422         var y = Roo.lib.Event.getPageY(e);
36423         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36424         
36425         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36426         var noAppend = tn.allowChildren === false;
36427         if(this.appendOnly || tn.parentNode.allowChildren === false){
36428             return noAppend ? false : "append";
36429         }
36430         var noBelow = false;
36431         if(!this.allowParentInsert){
36432             noBelow = tn.hasChildNodes() && tn.isExpanded();
36433         }
36434         var q = (b - t) / (noAppend ? 2 : 3);
36435         if(y >= t && y < (t + q)){
36436             return "above";
36437         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36438             return "below";
36439         }else{
36440             return "append";
36441         }
36442     },
36443     
36444     onNodeEnter : function(n, dd, e, data)
36445     {
36446         this.cancelExpand();
36447     },
36448     
36449     onNodeOver : function(n, dd, e, data)
36450     {
36451        
36452         var pt = this.getDropPoint(e, n, dd);
36453         var node = n.node;
36454         
36455         // auto node expand check
36456         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36457             this.queueExpand(node);
36458         }else if(pt != "append"){
36459             this.cancelExpand();
36460         }
36461         
36462         // set the insert point style on the target node
36463         var returnCls = this.dropNotAllowed;
36464         if(this.isValidDropPoint(n, pt, dd, e, data)){
36465            if(pt){
36466                var el = n.ddel;
36467                var cls;
36468                if(pt == "above"){
36469                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36470                    cls = "x-tree-drag-insert-above";
36471                }else if(pt == "below"){
36472                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36473                    cls = "x-tree-drag-insert-below";
36474                }else{
36475                    returnCls = "x-tree-drop-ok-append";
36476                    cls = "x-tree-drag-append";
36477                }
36478                if(this.lastInsertClass != cls){
36479                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36480                    this.lastInsertClass = cls;
36481                }
36482            }
36483        }
36484        return returnCls;
36485     },
36486     
36487     onNodeOut : function(n, dd, e, data){
36488         
36489         this.cancelExpand();
36490         this.removeDropIndicators(n);
36491     },
36492     
36493     onNodeDrop : function(n, dd, e, data){
36494         var point = this.getDropPoint(e, n, dd);
36495         var targetNode = n.node;
36496         targetNode.ui.startDrop();
36497         if(!this.isValidDropPoint(n, point, dd, e, data)){
36498             targetNode.ui.endDrop();
36499             return false;
36500         }
36501         // first try to find the drop node
36502         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36503         var dropEvent = {
36504             tree : this.tree,
36505             target: targetNode,
36506             data: data,
36507             point: point,
36508             source: dd,
36509             rawEvent: e,
36510             dropNode: dropNode,
36511             cancel: !dropNode   
36512         };
36513         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36514         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36515             targetNode.ui.endDrop();
36516             return false;
36517         }
36518         // allow target changing
36519         targetNode = dropEvent.target;
36520         if(point == "append" && !targetNode.isExpanded()){
36521             targetNode.expand(false, null, function(){
36522                 this.completeDrop(dropEvent);
36523             }.createDelegate(this));
36524         }else{
36525             this.completeDrop(dropEvent);
36526         }
36527         return true;
36528     },
36529     
36530     completeDrop : function(de){
36531         var ns = de.dropNode, p = de.point, t = de.target;
36532         if(!(ns instanceof Array)){
36533             ns = [ns];
36534         }
36535         var n;
36536         for(var i = 0, len = ns.length; i < len; i++){
36537             n = ns[i];
36538             if(p == "above"){
36539                 t.parentNode.insertBefore(n, t);
36540             }else if(p == "below"){
36541                 t.parentNode.insertBefore(n, t.nextSibling);
36542             }else{
36543                 t.appendChild(n);
36544             }
36545         }
36546         n.ui.focus();
36547         if(this.tree.hlDrop){
36548             n.ui.highlight();
36549         }
36550         t.ui.endDrop();
36551         this.tree.fireEvent("nodedrop", de);
36552     },
36553     
36554     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36555         if(this.tree.hlDrop){
36556             dropNode.ui.focus();
36557             dropNode.ui.highlight();
36558         }
36559         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36560     },
36561     
36562     getTree : function(){
36563         return this.tree;
36564     },
36565     
36566     removeDropIndicators : function(n){
36567         if(n && n.ddel){
36568             var el = n.ddel;
36569             Roo.fly(el).removeClass([
36570                     "x-tree-drag-insert-above",
36571                     "x-tree-drag-insert-below",
36572                     "x-tree-drag-append"]);
36573             this.lastInsertClass = "_noclass";
36574         }
36575     },
36576     
36577     beforeDragDrop : function(target, e, id){
36578         this.cancelExpand();
36579         return true;
36580     },
36581     
36582     afterRepair : function(data){
36583         if(data && Roo.enableFx){
36584             data.node.ui.highlight();
36585         }
36586         this.hideProxy();
36587     } 
36588     
36589 });
36590
36591 }
36592 /*
36593  * Based on:
36594  * Ext JS Library 1.1.1
36595  * Copyright(c) 2006-2007, Ext JS, LLC.
36596  *
36597  * Originally Released Under LGPL - original licence link has changed is not relivant.
36598  *
36599  * Fork - LGPL
36600  * <script type="text/javascript">
36601  */
36602  
36603
36604 if(Roo.dd.DragZone){
36605 Roo.tree.TreeDragZone = function(tree, config){
36606     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36607     this.tree = tree;
36608 };
36609
36610 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36611     ddGroup : "TreeDD",
36612    
36613     onBeforeDrag : function(data, e){
36614         var n = data.node;
36615         return n && n.draggable && !n.disabled;
36616     },
36617      
36618     
36619     onInitDrag : function(e){
36620         var data = this.dragData;
36621         this.tree.getSelectionModel().select(data.node);
36622         this.proxy.update("");
36623         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36624         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36625     },
36626     
36627     getRepairXY : function(e, data){
36628         return data.node.ui.getDDRepairXY();
36629     },
36630     
36631     onEndDrag : function(data, e){
36632         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36633         
36634         
36635     },
36636     
36637     onValidDrop : function(dd, e, id){
36638         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36639         this.hideProxy();
36640     },
36641     
36642     beforeInvalidDrop : function(e, id){
36643         // this scrolls the original position back into view
36644         var sm = this.tree.getSelectionModel();
36645         sm.clearSelections();
36646         sm.select(this.dragData.node);
36647     }
36648 });
36649 }/*
36650  * Based on:
36651  * Ext JS Library 1.1.1
36652  * Copyright(c) 2006-2007, Ext JS, LLC.
36653  *
36654  * Originally Released Under LGPL - original licence link has changed is not relivant.
36655  *
36656  * Fork - LGPL
36657  * <script type="text/javascript">
36658  */
36659 /**
36660  * @class Roo.tree.TreeEditor
36661  * @extends Roo.Editor
36662  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36663  * as the editor field.
36664  * @constructor
36665  * @param {Object} config (used to be the tree panel.)
36666  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36667  * 
36668  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36669  * @cfg {Roo.form.TextField|Object} field The field configuration
36670  *
36671  * 
36672  */
36673 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36674     var tree = config;
36675     var field;
36676     if (oldconfig) { // old style..
36677         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36678     } else {
36679         // new style..
36680         tree = config.tree;
36681         config.field = config.field  || {};
36682         config.field.xtype = 'TextField';
36683         field = Roo.factory(config.field, Roo.form);
36684     }
36685     config = config || {};
36686     
36687     
36688     this.addEvents({
36689         /**
36690          * @event beforenodeedit
36691          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36692          * false from the handler of this event.
36693          * @param {Editor} this
36694          * @param {Roo.tree.Node} node 
36695          */
36696         "beforenodeedit" : true
36697     });
36698     
36699     //Roo.log(config);
36700     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36701
36702     this.tree = tree;
36703
36704     tree.on('beforeclick', this.beforeNodeClick, this);
36705     tree.getTreeEl().on('mousedown', this.hide, this);
36706     this.on('complete', this.updateNode, this);
36707     this.on('beforestartedit', this.fitToTree, this);
36708     this.on('startedit', this.bindScroll, this, {delay:10});
36709     this.on('specialkey', this.onSpecialKey, this);
36710 };
36711
36712 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36713     /**
36714      * @cfg {String} alignment
36715      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36716      */
36717     alignment: "l-l",
36718     // inherit
36719     autoSize: false,
36720     /**
36721      * @cfg {Boolean} hideEl
36722      * True to hide the bound element while the editor is displayed (defaults to false)
36723      */
36724     hideEl : false,
36725     /**
36726      * @cfg {String} cls
36727      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36728      */
36729     cls: "x-small-editor x-tree-editor",
36730     /**
36731      * @cfg {Boolean} shim
36732      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36733      */
36734     shim:false,
36735     // inherit
36736     shadow:"frame",
36737     /**
36738      * @cfg {Number} maxWidth
36739      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36740      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36741      * scroll and client offsets into account prior to each edit.
36742      */
36743     maxWidth: 250,
36744
36745     editDelay : 350,
36746
36747     // private
36748     fitToTree : function(ed, el){
36749         var td = this.tree.getTreeEl().dom, nd = el.dom;
36750         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36751             td.scrollLeft = nd.offsetLeft;
36752         }
36753         var w = Math.min(
36754                 this.maxWidth,
36755                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36756         this.setSize(w, '');
36757         
36758         return this.fireEvent('beforenodeedit', this, this.editNode);
36759         
36760     },
36761
36762     // private
36763     triggerEdit : function(node){
36764         this.completeEdit();
36765         this.editNode = node;
36766         this.startEdit(node.ui.textNode, node.text);
36767     },
36768
36769     // private
36770     bindScroll : function(){
36771         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36772     },
36773
36774     // private
36775     beforeNodeClick : function(node, e){
36776         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36777         this.lastClick = new Date();
36778         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36779             e.stopEvent();
36780             this.triggerEdit(node);
36781             return false;
36782         }
36783         return true;
36784     },
36785
36786     // private
36787     updateNode : function(ed, value){
36788         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36789         this.editNode.setText(value);
36790     },
36791
36792     // private
36793     onHide : function(){
36794         Roo.tree.TreeEditor.superclass.onHide.call(this);
36795         if(this.editNode){
36796             this.editNode.ui.focus();
36797         }
36798     },
36799
36800     // private
36801     onSpecialKey : function(field, e){
36802         var k = e.getKey();
36803         if(k == e.ESC){
36804             e.stopEvent();
36805             this.cancelEdit();
36806         }else if(k == e.ENTER && !e.hasModifier()){
36807             e.stopEvent();
36808             this.completeEdit();
36809         }
36810     }
36811 });//<Script type="text/javascript">
36812 /*
36813  * Based on:
36814  * Ext JS Library 1.1.1
36815  * Copyright(c) 2006-2007, Ext JS, LLC.
36816  *
36817  * Originally Released Under LGPL - original licence link has changed is not relivant.
36818  *
36819  * Fork - LGPL
36820  * <script type="text/javascript">
36821  */
36822  
36823 /**
36824  * Not documented??? - probably should be...
36825  */
36826
36827 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36828     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36829     
36830     renderElements : function(n, a, targetNode, bulkRender){
36831         //consel.log("renderElements?");
36832         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36833
36834         var t = n.getOwnerTree();
36835         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
36836         
36837         var cols = t.columns;
36838         var bw = t.borderWidth;
36839         var c = cols[0];
36840         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36841          var cb = typeof a.checked == "boolean";
36842         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36843         var colcls = 'x-t-' + tid + '-c0';
36844         var buf = [
36845             '<li class="x-tree-node">',
36846             
36847                 
36848                 '<div class="x-tree-node-el ', a.cls,'">',
36849                     // extran...
36850                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
36851                 
36852                 
36853                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
36854                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
36855                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
36856                            (a.icon ? ' x-tree-node-inline-icon' : ''),
36857                            (a.iconCls ? ' '+a.iconCls : ''),
36858                            '" unselectable="on" />',
36859                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
36860                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
36861                              
36862                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36863                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
36864                             '<span unselectable="on" qtip="' + tx + '">',
36865                              tx,
36866                              '</span></a>' ,
36867                     '</div>',
36868                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
36869                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
36870                  ];
36871         for(var i = 1, len = cols.length; i < len; i++){
36872             c = cols[i];
36873             colcls = 'x-t-' + tid + '-c' +i;
36874             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
36875             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
36876                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
36877                       "</div>");
36878          }
36879          
36880          buf.push(
36881             '</a>',
36882             '<div class="x-clear"></div></div>',
36883             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36884             "</li>");
36885         
36886         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36887             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36888                                 n.nextSibling.ui.getEl(), buf.join(""));
36889         }else{
36890             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36891         }
36892         var el = this.wrap.firstChild;
36893         this.elRow = el;
36894         this.elNode = el.firstChild;
36895         this.ranchor = el.childNodes[1];
36896         this.ctNode = this.wrap.childNodes[1];
36897         var cs = el.firstChild.childNodes;
36898         this.indentNode = cs[0];
36899         this.ecNode = cs[1];
36900         this.iconNode = cs[2];
36901         var index = 3;
36902         if(cb){
36903             this.checkbox = cs[3];
36904             index++;
36905         }
36906         this.anchor = cs[index];
36907         
36908         this.textNode = cs[index].firstChild;
36909         
36910         //el.on("click", this.onClick, this);
36911         //el.on("dblclick", this.onDblClick, this);
36912         
36913         
36914        // console.log(this);
36915     },
36916     initEvents : function(){
36917         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
36918         
36919             
36920         var a = this.ranchor;
36921
36922         var el = Roo.get(a);
36923
36924         if(Roo.isOpera){ // opera render bug ignores the CSS
36925             el.setStyle("text-decoration", "none");
36926         }
36927
36928         el.on("click", this.onClick, this);
36929         el.on("dblclick", this.onDblClick, this);
36930         el.on("contextmenu", this.onContextMenu, this);
36931         
36932     },
36933     
36934     /*onSelectedChange : function(state){
36935         if(state){
36936             this.focus();
36937             this.addClass("x-tree-selected");
36938         }else{
36939             //this.blur();
36940             this.removeClass("x-tree-selected");
36941         }
36942     },*/
36943     addClass : function(cls){
36944         if(this.elRow){
36945             Roo.fly(this.elRow).addClass(cls);
36946         }
36947         
36948     },
36949     
36950     
36951     removeClass : function(cls){
36952         if(this.elRow){
36953             Roo.fly(this.elRow).removeClass(cls);
36954         }
36955     }
36956
36957     
36958     
36959 });//<Script type="text/javascript">
36960
36961 /*
36962  * Based on:
36963  * Ext JS Library 1.1.1
36964  * Copyright(c) 2006-2007, Ext JS, LLC.
36965  *
36966  * Originally Released Under LGPL - original licence link has changed is not relivant.
36967  *
36968  * Fork - LGPL
36969  * <script type="text/javascript">
36970  */
36971  
36972
36973 /**
36974  * @class Roo.tree.ColumnTree
36975  * @extends Roo.data.TreePanel
36976  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
36977  * @cfg {int} borderWidth  compined right/left border allowance
36978  * @constructor
36979  * @param {String/HTMLElement/Element} el The container element
36980  * @param {Object} config
36981  */
36982 Roo.tree.ColumnTree =  function(el, config)
36983 {
36984    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
36985    this.addEvents({
36986         /**
36987         * @event resize
36988         * Fire this event on a container when it resizes
36989         * @param {int} w Width
36990         * @param {int} h Height
36991         */
36992        "resize" : true
36993     });
36994     this.on('resize', this.onResize, this);
36995 };
36996
36997 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
36998     //lines:false,
36999     
37000     
37001     borderWidth: Roo.isBorderBox ? 0 : 2, 
37002     headEls : false,
37003     
37004     render : function(){
37005         // add the header.....
37006        
37007         Roo.tree.ColumnTree.superclass.render.apply(this);
37008         
37009         this.el.addClass('x-column-tree');
37010         
37011         this.headers = this.el.createChild(
37012             {cls:'x-tree-headers'},this.innerCt.dom);
37013    
37014         var cols = this.columns, c;
37015         var totalWidth = 0;
37016         this.headEls = [];
37017         var  len = cols.length;
37018         for(var i = 0; i < len; i++){
37019              c = cols[i];
37020              totalWidth += c.width;
37021             this.headEls.push(this.headers.createChild({
37022                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37023                  cn: {
37024                      cls:'x-tree-hd-text',
37025                      html: c.header
37026                  },
37027                  style:'width:'+(c.width-this.borderWidth)+'px;'
37028              }));
37029         }
37030         this.headers.createChild({cls:'x-clear'});
37031         // prevent floats from wrapping when clipped
37032         this.headers.setWidth(totalWidth);
37033         //this.innerCt.setWidth(totalWidth);
37034         this.innerCt.setStyle({ overflow: 'auto' });
37035         this.onResize(this.width, this.height);
37036              
37037         
37038     },
37039     onResize : function(w,h)
37040     {
37041         this.height = h;
37042         this.width = w;
37043         // resize cols..
37044         this.innerCt.setWidth(this.width);
37045         this.innerCt.setHeight(this.height-20);
37046         
37047         // headers...
37048         var cols = this.columns, c;
37049         var totalWidth = 0;
37050         var expEl = false;
37051         var len = cols.length;
37052         for(var i = 0; i < len; i++){
37053             c = cols[i];
37054             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37055                 // it's the expander..
37056                 expEl  = this.headEls[i];
37057                 continue;
37058             }
37059             totalWidth += c.width;
37060             
37061         }
37062         if (expEl) {
37063             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37064         }
37065         this.headers.setWidth(w-20);
37066
37067         
37068         
37069         
37070     }
37071 });
37072 /*
37073  * Based on:
37074  * Ext JS Library 1.1.1
37075  * Copyright(c) 2006-2007, Ext JS, LLC.
37076  *
37077  * Originally Released Under LGPL - original licence link has changed is not relivant.
37078  *
37079  * Fork - LGPL
37080  * <script type="text/javascript">
37081  */
37082  
37083 /**
37084  * @class Roo.menu.Menu
37085  * @extends Roo.util.Observable
37086  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37087  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37088  * @constructor
37089  * Creates a new Menu
37090  * @param {Object} config Configuration options
37091  */
37092 Roo.menu.Menu = function(config){
37093     Roo.apply(this, config);
37094     this.id = this.id || Roo.id();
37095     this.addEvents({
37096         /**
37097          * @event beforeshow
37098          * Fires before this menu is displayed
37099          * @param {Roo.menu.Menu} this
37100          */
37101         beforeshow : true,
37102         /**
37103          * @event beforehide
37104          * Fires before this menu is hidden
37105          * @param {Roo.menu.Menu} this
37106          */
37107         beforehide : true,
37108         /**
37109          * @event show
37110          * Fires after this menu is displayed
37111          * @param {Roo.menu.Menu} this
37112          */
37113         show : true,
37114         /**
37115          * @event hide
37116          * Fires after this menu is hidden
37117          * @param {Roo.menu.Menu} this
37118          */
37119         hide : true,
37120         /**
37121          * @event click
37122          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37123          * @param {Roo.menu.Menu} this
37124          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37125          * @param {Roo.EventObject} e
37126          */
37127         click : true,
37128         /**
37129          * @event mouseover
37130          * Fires when the mouse is hovering over this menu
37131          * @param {Roo.menu.Menu} this
37132          * @param {Roo.EventObject} e
37133          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37134          */
37135         mouseover : true,
37136         /**
37137          * @event mouseout
37138          * Fires when the mouse exits this menu
37139          * @param {Roo.menu.Menu} this
37140          * @param {Roo.EventObject} e
37141          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37142          */
37143         mouseout : true,
37144         /**
37145          * @event itemclick
37146          * Fires when a menu item contained in this menu is clicked
37147          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37148          * @param {Roo.EventObject} e
37149          */
37150         itemclick: true
37151     });
37152     if (this.registerMenu) {
37153         Roo.menu.MenuMgr.register(this);
37154     }
37155     
37156     var mis = this.items;
37157     this.items = new Roo.util.MixedCollection();
37158     if(mis){
37159         this.add.apply(this, mis);
37160     }
37161 };
37162
37163 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37164     /**
37165      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37166      */
37167     minWidth : 120,
37168     /**
37169      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37170      * for bottom-right shadow (defaults to "sides")
37171      */
37172     shadow : "sides",
37173     /**
37174      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37175      * this menu (defaults to "tl-tr?")
37176      */
37177     subMenuAlign : "tl-tr?",
37178     /**
37179      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37180      * relative to its element of origin (defaults to "tl-bl?")
37181      */
37182     defaultAlign : "tl-bl?",
37183     /**
37184      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37185      */
37186     allowOtherMenus : false,
37187     /**
37188      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37189      */
37190     registerMenu : true,
37191
37192     hidden:true,
37193
37194     // private
37195     render : function(){
37196         if(this.el){
37197             return;
37198         }
37199         var el = this.el = new Roo.Layer({
37200             cls: "x-menu",
37201             shadow:this.shadow,
37202             constrain: false,
37203             parentEl: this.parentEl || document.body,
37204             zindex:15000
37205         });
37206
37207         this.keyNav = new Roo.menu.MenuNav(this);
37208
37209         if(this.plain){
37210             el.addClass("x-menu-plain");
37211         }
37212         if(this.cls){
37213             el.addClass(this.cls);
37214         }
37215         // generic focus element
37216         this.focusEl = el.createChild({
37217             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37218         });
37219         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37220         //disabling touch- as it's causing issues ..
37221         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37222         ul.on('click'   , this.onClick, this);
37223         
37224         
37225         ul.on("mouseover", this.onMouseOver, this);
37226         ul.on("mouseout", this.onMouseOut, this);
37227         this.items.each(function(item){
37228             if (item.hidden) {
37229                 return;
37230             }
37231             
37232             var li = document.createElement("li");
37233             li.className = "x-menu-list-item";
37234             ul.dom.appendChild(li);
37235             item.render(li, this);
37236         }, this);
37237         this.ul = ul;
37238         this.autoWidth();
37239     },
37240
37241     // private
37242     autoWidth : function(){
37243         var el = this.el, ul = this.ul;
37244         if(!el){
37245             return;
37246         }
37247         var w = this.width;
37248         if(w){
37249             el.setWidth(w);
37250         }else if(Roo.isIE){
37251             el.setWidth(this.minWidth);
37252             var t = el.dom.offsetWidth; // force recalc
37253             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37254         }
37255     },
37256
37257     // private
37258     delayAutoWidth : function(){
37259         if(this.rendered){
37260             if(!this.awTask){
37261                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37262             }
37263             this.awTask.delay(20);
37264         }
37265     },
37266
37267     // private
37268     findTargetItem : function(e){
37269         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37270         if(t && t.menuItemId){
37271             return this.items.get(t.menuItemId);
37272         }
37273     },
37274
37275     // private
37276     onClick : function(e){
37277         Roo.log("menu.onClick");
37278         var t = this.findTargetItem(e);
37279         if(!t){
37280             return;
37281         }
37282         Roo.log(e);
37283         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37284             if(t == this.activeItem && t.shouldDeactivate(e)){
37285                 this.activeItem.deactivate();
37286                 delete this.activeItem;
37287                 return;
37288             }
37289             if(t.canActivate){
37290                 this.setActiveItem(t, true);
37291             }
37292             return;
37293             
37294             
37295         }
37296         
37297         t.onClick(e);
37298         this.fireEvent("click", this, t, e);
37299     },
37300
37301     // private
37302     setActiveItem : function(item, autoExpand){
37303         if(item != this.activeItem){
37304             if(this.activeItem){
37305                 this.activeItem.deactivate();
37306             }
37307             this.activeItem = item;
37308             item.activate(autoExpand);
37309         }else if(autoExpand){
37310             item.expandMenu();
37311         }
37312     },
37313
37314     // private
37315     tryActivate : function(start, step){
37316         var items = this.items;
37317         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37318             var item = items.get(i);
37319             if(!item.disabled && item.canActivate){
37320                 this.setActiveItem(item, false);
37321                 return item;
37322             }
37323         }
37324         return false;
37325     },
37326
37327     // private
37328     onMouseOver : function(e){
37329         var t;
37330         if(t = this.findTargetItem(e)){
37331             if(t.canActivate && !t.disabled){
37332                 this.setActiveItem(t, true);
37333             }
37334         }
37335         this.fireEvent("mouseover", this, e, t);
37336     },
37337
37338     // private
37339     onMouseOut : function(e){
37340         var t;
37341         if(t = this.findTargetItem(e)){
37342             if(t == this.activeItem && t.shouldDeactivate(e)){
37343                 this.activeItem.deactivate();
37344                 delete this.activeItem;
37345             }
37346         }
37347         this.fireEvent("mouseout", this, e, t);
37348     },
37349
37350     /**
37351      * Read-only.  Returns true if the menu is currently displayed, else false.
37352      * @type Boolean
37353      */
37354     isVisible : function(){
37355         return this.el && !this.hidden;
37356     },
37357
37358     /**
37359      * Displays this menu relative to another element
37360      * @param {String/HTMLElement/Roo.Element} element The element to align to
37361      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37362      * the element (defaults to this.defaultAlign)
37363      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37364      */
37365     show : function(el, pos, parentMenu){
37366         this.parentMenu = parentMenu;
37367         if(!this.el){
37368             this.render();
37369         }
37370         this.fireEvent("beforeshow", this);
37371         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37372     },
37373
37374     /**
37375      * Displays this menu at a specific xy position
37376      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37377      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37378      */
37379     showAt : function(xy, parentMenu, /* private: */_e){
37380         this.parentMenu = parentMenu;
37381         if(!this.el){
37382             this.render();
37383         }
37384         if(_e !== false){
37385             this.fireEvent("beforeshow", this);
37386             xy = this.el.adjustForConstraints(xy);
37387         }
37388         this.el.setXY(xy);
37389         this.el.show();
37390         this.hidden = false;
37391         this.focus();
37392         this.fireEvent("show", this);
37393     },
37394
37395     focus : function(){
37396         if(!this.hidden){
37397             this.doFocus.defer(50, this);
37398         }
37399     },
37400
37401     doFocus : function(){
37402         if(!this.hidden){
37403             this.focusEl.focus();
37404         }
37405     },
37406
37407     /**
37408      * Hides this menu and optionally all parent menus
37409      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37410      */
37411     hide : function(deep){
37412         if(this.el && this.isVisible()){
37413             this.fireEvent("beforehide", this);
37414             if(this.activeItem){
37415                 this.activeItem.deactivate();
37416                 this.activeItem = null;
37417             }
37418             this.el.hide();
37419             this.hidden = true;
37420             this.fireEvent("hide", this);
37421         }
37422         if(deep === true && this.parentMenu){
37423             this.parentMenu.hide(true);
37424         }
37425     },
37426
37427     /**
37428      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37429      * Any of the following are valid:
37430      * <ul>
37431      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37432      * <li>An HTMLElement object which will be converted to a menu item</li>
37433      * <li>A menu item config object that will be created as a new menu item</li>
37434      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37435      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37436      * </ul>
37437      * Usage:
37438      * <pre><code>
37439 // Create the menu
37440 var menu = new Roo.menu.Menu();
37441
37442 // Create a menu item to add by reference
37443 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37444
37445 // Add a bunch of items at once using different methods.
37446 // Only the last item added will be returned.
37447 var item = menu.add(
37448     menuItem,                // add existing item by ref
37449     'Dynamic Item',          // new TextItem
37450     '-',                     // new separator
37451     { text: 'Config Item' }  // new item by config
37452 );
37453 </code></pre>
37454      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37455      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37456      */
37457     add : function(){
37458         var a = arguments, l = a.length, item;
37459         for(var i = 0; i < l; i++){
37460             var el = a[i];
37461             if ((typeof(el) == "object") && el.xtype && el.xns) {
37462                 el = Roo.factory(el, Roo.menu);
37463             }
37464             
37465             if(el.render){ // some kind of Item
37466                 item = this.addItem(el);
37467             }else if(typeof el == "string"){ // string
37468                 if(el == "separator" || el == "-"){
37469                     item = this.addSeparator();
37470                 }else{
37471                     item = this.addText(el);
37472                 }
37473             }else if(el.tagName || el.el){ // element
37474                 item = this.addElement(el);
37475             }else if(typeof el == "object"){ // must be menu item config?
37476                 item = this.addMenuItem(el);
37477             }
37478         }
37479         return item;
37480     },
37481
37482     /**
37483      * Returns this menu's underlying {@link Roo.Element} object
37484      * @return {Roo.Element} The element
37485      */
37486     getEl : function(){
37487         if(!this.el){
37488             this.render();
37489         }
37490         return this.el;
37491     },
37492
37493     /**
37494      * Adds a separator bar to the menu
37495      * @return {Roo.menu.Item} The menu item that was added
37496      */
37497     addSeparator : function(){
37498         return this.addItem(new Roo.menu.Separator());
37499     },
37500
37501     /**
37502      * Adds an {@link Roo.Element} object to the menu
37503      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37504      * @return {Roo.menu.Item} The menu item that was added
37505      */
37506     addElement : function(el){
37507         return this.addItem(new Roo.menu.BaseItem(el));
37508     },
37509
37510     /**
37511      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37512      * @param {Roo.menu.Item} item The menu item to add
37513      * @return {Roo.menu.Item} The menu item that was added
37514      */
37515     addItem : function(item){
37516         this.items.add(item);
37517         if(this.ul){
37518             var li = document.createElement("li");
37519             li.className = "x-menu-list-item";
37520             this.ul.dom.appendChild(li);
37521             item.render(li, this);
37522             this.delayAutoWidth();
37523         }
37524         return item;
37525     },
37526
37527     /**
37528      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37529      * @param {Object} config A MenuItem config object
37530      * @return {Roo.menu.Item} The menu item that was added
37531      */
37532     addMenuItem : function(config){
37533         if(!(config instanceof Roo.menu.Item)){
37534             if(typeof config.checked == "boolean"){ // must be check menu item config?
37535                 config = new Roo.menu.CheckItem(config);
37536             }else{
37537                 config = new Roo.menu.Item(config);
37538             }
37539         }
37540         return this.addItem(config);
37541     },
37542
37543     /**
37544      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37545      * @param {String} text The text to display in the menu item
37546      * @return {Roo.menu.Item} The menu item that was added
37547      */
37548     addText : function(text){
37549         return this.addItem(new Roo.menu.TextItem({ text : text }));
37550     },
37551
37552     /**
37553      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37554      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37555      * @param {Roo.menu.Item} item The menu item to add
37556      * @return {Roo.menu.Item} The menu item that was added
37557      */
37558     insert : function(index, item){
37559         this.items.insert(index, item);
37560         if(this.ul){
37561             var li = document.createElement("li");
37562             li.className = "x-menu-list-item";
37563             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37564             item.render(li, this);
37565             this.delayAutoWidth();
37566         }
37567         return item;
37568     },
37569
37570     /**
37571      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37572      * @param {Roo.menu.Item} item The menu item to remove
37573      */
37574     remove : function(item){
37575         this.items.removeKey(item.id);
37576         item.destroy();
37577     },
37578
37579     /**
37580      * Removes and destroys all items in the menu
37581      */
37582     removeAll : function(){
37583         var f;
37584         while(f = this.items.first()){
37585             this.remove(f);
37586         }
37587     }
37588 });
37589
37590 // MenuNav is a private utility class used internally by the Menu
37591 Roo.menu.MenuNav = function(menu){
37592     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37593     this.scope = this.menu = menu;
37594 };
37595
37596 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37597     doRelay : function(e, h){
37598         var k = e.getKey();
37599         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37600             this.menu.tryActivate(0, 1);
37601             return false;
37602         }
37603         return h.call(this.scope || this, e, this.menu);
37604     },
37605
37606     up : function(e, m){
37607         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37608             m.tryActivate(m.items.length-1, -1);
37609         }
37610     },
37611
37612     down : function(e, m){
37613         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37614             m.tryActivate(0, 1);
37615         }
37616     },
37617
37618     right : function(e, m){
37619         if(m.activeItem){
37620             m.activeItem.expandMenu(true);
37621         }
37622     },
37623
37624     left : function(e, m){
37625         m.hide();
37626         if(m.parentMenu && m.parentMenu.activeItem){
37627             m.parentMenu.activeItem.activate();
37628         }
37629     },
37630
37631     enter : function(e, m){
37632         if(m.activeItem){
37633             e.stopPropagation();
37634             m.activeItem.onClick(e);
37635             m.fireEvent("click", this, m.activeItem);
37636             return true;
37637         }
37638     }
37639 });/*
37640  * Based on:
37641  * Ext JS Library 1.1.1
37642  * Copyright(c) 2006-2007, Ext JS, LLC.
37643  *
37644  * Originally Released Under LGPL - original licence link has changed is not relivant.
37645  *
37646  * Fork - LGPL
37647  * <script type="text/javascript">
37648  */
37649  
37650 /**
37651  * @class Roo.menu.MenuMgr
37652  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37653  * @singleton
37654  */
37655 Roo.menu.MenuMgr = function(){
37656    var menus, active, groups = {}, attached = false, lastShow = new Date();
37657
37658    // private - called when first menu is created
37659    function init(){
37660        menus = {};
37661        active = new Roo.util.MixedCollection();
37662        Roo.get(document).addKeyListener(27, function(){
37663            if(active.length > 0){
37664                hideAll();
37665            }
37666        });
37667    }
37668
37669    // private
37670    function hideAll(){
37671        if(active && active.length > 0){
37672            var c = active.clone();
37673            c.each(function(m){
37674                m.hide();
37675            });
37676        }
37677    }
37678
37679    // private
37680    function onHide(m){
37681        active.remove(m);
37682        if(active.length < 1){
37683            Roo.get(document).un("mousedown", onMouseDown);
37684            attached = false;
37685        }
37686    }
37687
37688    // private
37689    function onShow(m){
37690        var last = active.last();
37691        lastShow = new Date();
37692        active.add(m);
37693        if(!attached){
37694            Roo.get(document).on("mousedown", onMouseDown);
37695            attached = true;
37696        }
37697        if(m.parentMenu){
37698           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37699           m.parentMenu.activeChild = m;
37700        }else if(last && last.isVisible()){
37701           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37702        }
37703    }
37704
37705    // private
37706    function onBeforeHide(m){
37707        if(m.activeChild){
37708            m.activeChild.hide();
37709        }
37710        if(m.autoHideTimer){
37711            clearTimeout(m.autoHideTimer);
37712            delete m.autoHideTimer;
37713        }
37714    }
37715
37716    // private
37717    function onBeforeShow(m){
37718        var pm = m.parentMenu;
37719        if(!pm && !m.allowOtherMenus){
37720            hideAll();
37721        }else if(pm && pm.activeChild && active != m){
37722            pm.activeChild.hide();
37723        }
37724    }
37725
37726    // private
37727    function onMouseDown(e){
37728        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37729            hideAll();
37730        }
37731    }
37732
37733    // private
37734    function onBeforeCheck(mi, state){
37735        if(state){
37736            var g = groups[mi.group];
37737            for(var i = 0, l = g.length; i < l; i++){
37738                if(g[i] != mi){
37739                    g[i].setChecked(false);
37740                }
37741            }
37742        }
37743    }
37744
37745    return {
37746
37747        /**
37748         * Hides all menus that are currently visible
37749         */
37750        hideAll : function(){
37751             hideAll();  
37752        },
37753
37754        // private
37755        register : function(menu){
37756            if(!menus){
37757                init();
37758            }
37759            menus[menu.id] = menu;
37760            menu.on("beforehide", onBeforeHide);
37761            menu.on("hide", onHide);
37762            menu.on("beforeshow", onBeforeShow);
37763            menu.on("show", onShow);
37764            var g = menu.group;
37765            if(g && menu.events["checkchange"]){
37766                if(!groups[g]){
37767                    groups[g] = [];
37768                }
37769                groups[g].push(menu);
37770                menu.on("checkchange", onCheck);
37771            }
37772        },
37773
37774         /**
37775          * Returns a {@link Roo.menu.Menu} object
37776          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37777          * be used to generate and return a new Menu instance.
37778          */
37779        get : function(menu){
37780            if(typeof menu == "string"){ // menu id
37781                return menus[menu];
37782            }else if(menu.events){  // menu instance
37783                return menu;
37784            }else if(typeof menu.length == 'number'){ // array of menu items?
37785                return new Roo.menu.Menu({items:menu});
37786            }else{ // otherwise, must be a config
37787                return new Roo.menu.Menu(menu);
37788            }
37789        },
37790
37791        // private
37792        unregister : function(menu){
37793            delete menus[menu.id];
37794            menu.un("beforehide", onBeforeHide);
37795            menu.un("hide", onHide);
37796            menu.un("beforeshow", onBeforeShow);
37797            menu.un("show", onShow);
37798            var g = menu.group;
37799            if(g && menu.events["checkchange"]){
37800                groups[g].remove(menu);
37801                menu.un("checkchange", onCheck);
37802            }
37803        },
37804
37805        // private
37806        registerCheckable : function(menuItem){
37807            var g = menuItem.group;
37808            if(g){
37809                if(!groups[g]){
37810                    groups[g] = [];
37811                }
37812                groups[g].push(menuItem);
37813                menuItem.on("beforecheckchange", onBeforeCheck);
37814            }
37815        },
37816
37817        // private
37818        unregisterCheckable : function(menuItem){
37819            var g = menuItem.group;
37820            if(g){
37821                groups[g].remove(menuItem);
37822                menuItem.un("beforecheckchange", onBeforeCheck);
37823            }
37824        }
37825    };
37826 }();/*
37827  * Based on:
37828  * Ext JS Library 1.1.1
37829  * Copyright(c) 2006-2007, Ext JS, LLC.
37830  *
37831  * Originally Released Under LGPL - original licence link has changed is not relivant.
37832  *
37833  * Fork - LGPL
37834  * <script type="text/javascript">
37835  */
37836  
37837
37838 /**
37839  * @class Roo.menu.BaseItem
37840  * @extends Roo.Component
37841  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
37842  * management and base configuration options shared by all menu components.
37843  * @constructor
37844  * Creates a new BaseItem
37845  * @param {Object} config Configuration options
37846  */
37847 Roo.menu.BaseItem = function(config){
37848     Roo.menu.BaseItem.superclass.constructor.call(this, config);
37849
37850     this.addEvents({
37851         /**
37852          * @event click
37853          * Fires when this item is clicked
37854          * @param {Roo.menu.BaseItem} this
37855          * @param {Roo.EventObject} e
37856          */
37857         click: true,
37858         /**
37859          * @event activate
37860          * Fires when this item is activated
37861          * @param {Roo.menu.BaseItem} this
37862          */
37863         activate : true,
37864         /**
37865          * @event deactivate
37866          * Fires when this item is deactivated
37867          * @param {Roo.menu.BaseItem} this
37868          */
37869         deactivate : true
37870     });
37871
37872     if(this.handler){
37873         this.on("click", this.handler, this.scope, true);
37874     }
37875 };
37876
37877 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
37878     /**
37879      * @cfg {Function} handler
37880      * A function that will handle the click event of this menu item (defaults to undefined)
37881      */
37882     /**
37883      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
37884      */
37885     canActivate : false,
37886     
37887      /**
37888      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
37889      */
37890     hidden: false,
37891     
37892     /**
37893      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
37894      */
37895     activeClass : "x-menu-item-active",
37896     /**
37897      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
37898      */
37899     hideOnClick : true,
37900     /**
37901      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
37902      */
37903     hideDelay : 100,
37904
37905     // private
37906     ctype: "Roo.menu.BaseItem",
37907
37908     // private
37909     actionMode : "container",
37910
37911     // private
37912     render : function(container, parentMenu){
37913         this.parentMenu = parentMenu;
37914         Roo.menu.BaseItem.superclass.render.call(this, container);
37915         this.container.menuItemId = this.id;
37916     },
37917
37918     // private
37919     onRender : function(container, position){
37920         this.el = Roo.get(this.el);
37921         container.dom.appendChild(this.el.dom);
37922     },
37923
37924     // private
37925     onClick : function(e){
37926         if(!this.disabled && this.fireEvent("click", this, e) !== false
37927                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
37928             this.handleClick(e);
37929         }else{
37930             e.stopEvent();
37931         }
37932     },
37933
37934     // private
37935     activate : function(){
37936         if(this.disabled){
37937             return false;
37938         }
37939         var li = this.container;
37940         li.addClass(this.activeClass);
37941         this.region = li.getRegion().adjust(2, 2, -2, -2);
37942         this.fireEvent("activate", this);
37943         return true;
37944     },
37945
37946     // private
37947     deactivate : function(){
37948         this.container.removeClass(this.activeClass);
37949         this.fireEvent("deactivate", this);
37950     },
37951
37952     // private
37953     shouldDeactivate : function(e){
37954         return !this.region || !this.region.contains(e.getPoint());
37955     },
37956
37957     // private
37958     handleClick : function(e){
37959         if(this.hideOnClick){
37960             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
37961         }
37962     },
37963
37964     // private
37965     expandMenu : function(autoActivate){
37966         // do nothing
37967     },
37968
37969     // private
37970     hideMenu : function(){
37971         // do nothing
37972     }
37973 });/*
37974  * Based on:
37975  * Ext JS Library 1.1.1
37976  * Copyright(c) 2006-2007, Ext JS, LLC.
37977  *
37978  * Originally Released Under LGPL - original licence link has changed is not relivant.
37979  *
37980  * Fork - LGPL
37981  * <script type="text/javascript">
37982  */
37983  
37984 /**
37985  * @class Roo.menu.Adapter
37986  * @extends Roo.menu.BaseItem
37987  * 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.
37988  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
37989  * @constructor
37990  * Creates a new Adapter
37991  * @param {Object} config Configuration options
37992  */
37993 Roo.menu.Adapter = function(component, config){
37994     Roo.menu.Adapter.superclass.constructor.call(this, config);
37995     this.component = component;
37996 };
37997 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
37998     // private
37999     canActivate : true,
38000
38001     // private
38002     onRender : function(container, position){
38003         this.component.render(container);
38004         this.el = this.component.getEl();
38005     },
38006
38007     // private
38008     activate : function(){
38009         if(this.disabled){
38010             return false;
38011         }
38012         this.component.focus();
38013         this.fireEvent("activate", this);
38014         return true;
38015     },
38016
38017     // private
38018     deactivate : function(){
38019         this.fireEvent("deactivate", this);
38020     },
38021
38022     // private
38023     disable : function(){
38024         this.component.disable();
38025         Roo.menu.Adapter.superclass.disable.call(this);
38026     },
38027
38028     // private
38029     enable : function(){
38030         this.component.enable();
38031         Roo.menu.Adapter.superclass.enable.call(this);
38032     }
38033 });/*
38034  * Based on:
38035  * Ext JS Library 1.1.1
38036  * Copyright(c) 2006-2007, Ext JS, LLC.
38037  *
38038  * Originally Released Under LGPL - original licence link has changed is not relivant.
38039  *
38040  * Fork - LGPL
38041  * <script type="text/javascript">
38042  */
38043
38044 /**
38045  * @class Roo.menu.TextItem
38046  * @extends Roo.menu.BaseItem
38047  * Adds a static text string to a menu, usually used as either a heading or group separator.
38048  * Note: old style constructor with text is still supported.
38049  * 
38050  * @constructor
38051  * Creates a new TextItem
38052  * @param {Object} cfg Configuration
38053  */
38054 Roo.menu.TextItem = function(cfg){
38055     if (typeof(cfg) == 'string') {
38056         this.text = cfg;
38057     } else {
38058         Roo.apply(this,cfg);
38059     }
38060     
38061     Roo.menu.TextItem.superclass.constructor.call(this);
38062 };
38063
38064 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38065     /**
38066      * @cfg {Boolean} text Text to show on item.
38067      */
38068     text : '',
38069     
38070     /**
38071      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38072      */
38073     hideOnClick : false,
38074     /**
38075      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38076      */
38077     itemCls : "x-menu-text",
38078
38079     // private
38080     onRender : function(){
38081         var s = document.createElement("span");
38082         s.className = this.itemCls;
38083         s.innerHTML = this.text;
38084         this.el = s;
38085         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38086     }
38087 });/*
38088  * Based on:
38089  * Ext JS Library 1.1.1
38090  * Copyright(c) 2006-2007, Ext JS, LLC.
38091  *
38092  * Originally Released Under LGPL - original licence link has changed is not relivant.
38093  *
38094  * Fork - LGPL
38095  * <script type="text/javascript">
38096  */
38097
38098 /**
38099  * @class Roo.menu.Separator
38100  * @extends Roo.menu.BaseItem
38101  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38102  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38103  * @constructor
38104  * @param {Object} config Configuration options
38105  */
38106 Roo.menu.Separator = function(config){
38107     Roo.menu.Separator.superclass.constructor.call(this, config);
38108 };
38109
38110 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38111     /**
38112      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38113      */
38114     itemCls : "x-menu-sep",
38115     /**
38116      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38117      */
38118     hideOnClick : false,
38119
38120     // private
38121     onRender : function(li){
38122         var s = document.createElement("span");
38123         s.className = this.itemCls;
38124         s.innerHTML = "&#160;";
38125         this.el = s;
38126         li.addClass("x-menu-sep-li");
38127         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38128     }
38129 });/*
38130  * Based on:
38131  * Ext JS Library 1.1.1
38132  * Copyright(c) 2006-2007, Ext JS, LLC.
38133  *
38134  * Originally Released Under LGPL - original licence link has changed is not relivant.
38135  *
38136  * Fork - LGPL
38137  * <script type="text/javascript">
38138  */
38139 /**
38140  * @class Roo.menu.Item
38141  * @extends Roo.menu.BaseItem
38142  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38143  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38144  * activation and click handling.
38145  * @constructor
38146  * Creates a new Item
38147  * @param {Object} config Configuration options
38148  */
38149 Roo.menu.Item = function(config){
38150     Roo.menu.Item.superclass.constructor.call(this, config);
38151     if(this.menu){
38152         this.menu = Roo.menu.MenuMgr.get(this.menu);
38153     }
38154 };
38155 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38156     
38157     /**
38158      * @cfg {String} text
38159      * The text to show on the menu item.
38160      */
38161     text: '',
38162      /**
38163      * @cfg {String} HTML to render in menu
38164      * The text to show on the menu item (HTML version).
38165      */
38166     html: '',
38167     /**
38168      * @cfg {String} icon
38169      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38170      */
38171     icon: undefined,
38172     /**
38173      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38174      */
38175     itemCls : "x-menu-item",
38176     /**
38177      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38178      */
38179     canActivate : true,
38180     /**
38181      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38182      */
38183     showDelay: 200,
38184     // doc'd in BaseItem
38185     hideDelay: 200,
38186
38187     // private
38188     ctype: "Roo.menu.Item",
38189     
38190     // private
38191     onRender : function(container, position){
38192         var el = document.createElement("a");
38193         el.hideFocus = true;
38194         el.unselectable = "on";
38195         el.href = this.href || "#";
38196         if(this.hrefTarget){
38197             el.target = this.hrefTarget;
38198         }
38199         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38200         
38201         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38202         
38203         el.innerHTML = String.format(
38204                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38205                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38206         this.el = el;
38207         Roo.menu.Item.superclass.onRender.call(this, container, position);
38208     },
38209
38210     /**
38211      * Sets the text to display in this menu item
38212      * @param {String} text The text to display
38213      * @param {Boolean} isHTML true to indicate text is pure html.
38214      */
38215     setText : function(text, isHTML){
38216         if (isHTML) {
38217             this.html = text;
38218         } else {
38219             this.text = text;
38220             this.html = '';
38221         }
38222         if(this.rendered){
38223             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38224      
38225             this.el.update(String.format(
38226                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38227                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38228             this.parentMenu.autoWidth();
38229         }
38230     },
38231
38232     // private
38233     handleClick : function(e){
38234         if(!this.href){ // if no link defined, stop the event automatically
38235             e.stopEvent();
38236         }
38237         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38238     },
38239
38240     // private
38241     activate : function(autoExpand){
38242         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38243             this.focus();
38244             if(autoExpand){
38245                 this.expandMenu();
38246             }
38247         }
38248         return true;
38249     },
38250
38251     // private
38252     shouldDeactivate : function(e){
38253         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38254             if(this.menu && this.menu.isVisible()){
38255                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38256             }
38257             return true;
38258         }
38259         return false;
38260     },
38261
38262     // private
38263     deactivate : function(){
38264         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38265         this.hideMenu();
38266     },
38267
38268     // private
38269     expandMenu : function(autoActivate){
38270         if(!this.disabled && this.menu){
38271             clearTimeout(this.hideTimer);
38272             delete this.hideTimer;
38273             if(!this.menu.isVisible() && !this.showTimer){
38274                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38275             }else if (this.menu.isVisible() && autoActivate){
38276                 this.menu.tryActivate(0, 1);
38277             }
38278         }
38279     },
38280
38281     // private
38282     deferExpand : function(autoActivate){
38283         delete this.showTimer;
38284         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38285         if(autoActivate){
38286             this.menu.tryActivate(0, 1);
38287         }
38288     },
38289
38290     // private
38291     hideMenu : function(){
38292         clearTimeout(this.showTimer);
38293         delete this.showTimer;
38294         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38295             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38296         }
38297     },
38298
38299     // private
38300     deferHide : function(){
38301         delete this.hideTimer;
38302         this.menu.hide();
38303     }
38304 });/*
38305  * Based on:
38306  * Ext JS Library 1.1.1
38307  * Copyright(c) 2006-2007, Ext JS, LLC.
38308  *
38309  * Originally Released Under LGPL - original licence link has changed is not relivant.
38310  *
38311  * Fork - LGPL
38312  * <script type="text/javascript">
38313  */
38314  
38315 /**
38316  * @class Roo.menu.CheckItem
38317  * @extends Roo.menu.Item
38318  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38319  * @constructor
38320  * Creates a new CheckItem
38321  * @param {Object} config Configuration options
38322  */
38323 Roo.menu.CheckItem = function(config){
38324     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38325     this.addEvents({
38326         /**
38327          * @event beforecheckchange
38328          * Fires before the checked value is set, providing an opportunity to cancel if needed
38329          * @param {Roo.menu.CheckItem} this
38330          * @param {Boolean} checked The new checked value that will be set
38331          */
38332         "beforecheckchange" : true,
38333         /**
38334          * @event checkchange
38335          * Fires after the checked value has been set
38336          * @param {Roo.menu.CheckItem} this
38337          * @param {Boolean} checked The checked value that was set
38338          */
38339         "checkchange" : true
38340     });
38341     if(this.checkHandler){
38342         this.on('checkchange', this.checkHandler, this.scope);
38343     }
38344 };
38345 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38346     /**
38347      * @cfg {String} group
38348      * All check items with the same group name will automatically be grouped into a single-select
38349      * radio button group (defaults to '')
38350      */
38351     /**
38352      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38353      */
38354     itemCls : "x-menu-item x-menu-check-item",
38355     /**
38356      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38357      */
38358     groupClass : "x-menu-group-item",
38359
38360     /**
38361      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38362      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38363      * initialized with checked = true will be rendered as checked.
38364      */
38365     checked: false,
38366
38367     // private
38368     ctype: "Roo.menu.CheckItem",
38369
38370     // private
38371     onRender : function(c){
38372         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38373         if(this.group){
38374             this.el.addClass(this.groupClass);
38375         }
38376         Roo.menu.MenuMgr.registerCheckable(this);
38377         if(this.checked){
38378             this.checked = false;
38379             this.setChecked(true, true);
38380         }
38381     },
38382
38383     // private
38384     destroy : function(){
38385         if(this.rendered){
38386             Roo.menu.MenuMgr.unregisterCheckable(this);
38387         }
38388         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38389     },
38390
38391     /**
38392      * Set the checked state of this item
38393      * @param {Boolean} checked The new checked value
38394      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38395      */
38396     setChecked : function(state, suppressEvent){
38397         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38398             if(this.container){
38399                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38400             }
38401             this.checked = state;
38402             if(suppressEvent !== true){
38403                 this.fireEvent("checkchange", this, state);
38404             }
38405         }
38406     },
38407
38408     // private
38409     handleClick : function(e){
38410        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38411            this.setChecked(!this.checked);
38412        }
38413        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38414     }
38415 });/*
38416  * Based on:
38417  * Ext JS Library 1.1.1
38418  * Copyright(c) 2006-2007, Ext JS, LLC.
38419  *
38420  * Originally Released Under LGPL - original licence link has changed is not relivant.
38421  *
38422  * Fork - LGPL
38423  * <script type="text/javascript">
38424  */
38425  
38426 /**
38427  * @class Roo.menu.DateItem
38428  * @extends Roo.menu.Adapter
38429  * A menu item that wraps the {@link Roo.DatPicker} component.
38430  * @constructor
38431  * Creates a new DateItem
38432  * @param {Object} config Configuration options
38433  */
38434 Roo.menu.DateItem = function(config){
38435     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38436     /** The Roo.DatePicker object @type Roo.DatePicker */
38437     this.picker = this.component;
38438     this.addEvents({select: true});
38439     
38440     this.picker.on("render", function(picker){
38441         picker.getEl().swallowEvent("click");
38442         picker.container.addClass("x-menu-date-item");
38443     });
38444
38445     this.picker.on("select", this.onSelect, this);
38446 };
38447
38448 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38449     // private
38450     onSelect : function(picker, date){
38451         this.fireEvent("select", this, date, picker);
38452         Roo.menu.DateItem.superclass.handleClick.call(this);
38453     }
38454 });/*
38455  * Based on:
38456  * Ext JS Library 1.1.1
38457  * Copyright(c) 2006-2007, Ext JS, LLC.
38458  *
38459  * Originally Released Under LGPL - original licence link has changed is not relivant.
38460  *
38461  * Fork - LGPL
38462  * <script type="text/javascript">
38463  */
38464  
38465 /**
38466  * @class Roo.menu.ColorItem
38467  * @extends Roo.menu.Adapter
38468  * A menu item that wraps the {@link Roo.ColorPalette} component.
38469  * @constructor
38470  * Creates a new ColorItem
38471  * @param {Object} config Configuration options
38472  */
38473 Roo.menu.ColorItem = function(config){
38474     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38475     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38476     this.palette = this.component;
38477     this.relayEvents(this.palette, ["select"]);
38478     if(this.selectHandler){
38479         this.on('select', this.selectHandler, this.scope);
38480     }
38481 };
38482 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38483  * Based on:
38484  * Ext JS Library 1.1.1
38485  * Copyright(c) 2006-2007, Ext JS, LLC.
38486  *
38487  * Originally Released Under LGPL - original licence link has changed is not relivant.
38488  *
38489  * Fork - LGPL
38490  * <script type="text/javascript">
38491  */
38492  
38493
38494 /**
38495  * @class Roo.menu.DateMenu
38496  * @extends Roo.menu.Menu
38497  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38498  * @constructor
38499  * Creates a new DateMenu
38500  * @param {Object} config Configuration options
38501  */
38502 Roo.menu.DateMenu = function(config){
38503     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38504     this.plain = true;
38505     var di = new Roo.menu.DateItem(config);
38506     this.add(di);
38507     /**
38508      * The {@link Roo.DatePicker} instance for this DateMenu
38509      * @type DatePicker
38510      */
38511     this.picker = di.picker;
38512     /**
38513      * @event select
38514      * @param {DatePicker} picker
38515      * @param {Date} date
38516      */
38517     this.relayEvents(di, ["select"]);
38518     this.on('beforeshow', function(){
38519         if(this.picker){
38520             this.picker.hideMonthPicker(false);
38521         }
38522     }, this);
38523 };
38524 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38525     cls:'x-date-menu'
38526 });/*
38527  * Based on:
38528  * Ext JS Library 1.1.1
38529  * Copyright(c) 2006-2007, Ext JS, LLC.
38530  *
38531  * Originally Released Under LGPL - original licence link has changed is not relivant.
38532  *
38533  * Fork - LGPL
38534  * <script type="text/javascript">
38535  */
38536  
38537
38538 /**
38539  * @class Roo.menu.ColorMenu
38540  * @extends Roo.menu.Menu
38541  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38542  * @constructor
38543  * Creates a new ColorMenu
38544  * @param {Object} config Configuration options
38545  */
38546 Roo.menu.ColorMenu = function(config){
38547     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38548     this.plain = true;
38549     var ci = new Roo.menu.ColorItem(config);
38550     this.add(ci);
38551     /**
38552      * The {@link Roo.ColorPalette} instance for this ColorMenu
38553      * @type ColorPalette
38554      */
38555     this.palette = ci.palette;
38556     /**
38557      * @event select
38558      * @param {ColorPalette} palette
38559      * @param {String} color
38560      */
38561     this.relayEvents(ci, ["select"]);
38562 };
38563 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38564  * Based on:
38565  * Ext JS Library 1.1.1
38566  * Copyright(c) 2006-2007, Ext JS, LLC.
38567  *
38568  * Originally Released Under LGPL - original licence link has changed is not relivant.
38569  *
38570  * Fork - LGPL
38571  * <script type="text/javascript">
38572  */
38573  
38574 /**
38575  * @class Roo.form.Field
38576  * @extends Roo.BoxComponent
38577  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38578  * @constructor
38579  * Creates a new Field
38580  * @param {Object} config Configuration options
38581  */
38582 Roo.form.Field = function(config){
38583     Roo.form.Field.superclass.constructor.call(this, config);
38584 };
38585
38586 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38587     /**
38588      * @cfg {String} fieldLabel Label to use when rendering a form.
38589      */
38590        /**
38591      * @cfg {String} qtip Mouse over tip
38592      */
38593      
38594     /**
38595      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38596      */
38597     invalidClass : "x-form-invalid",
38598     /**
38599      * @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")
38600      */
38601     invalidText : "The value in this field is invalid",
38602     /**
38603      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38604      */
38605     focusClass : "x-form-focus",
38606     /**
38607      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38608       automatic validation (defaults to "keyup").
38609      */
38610     validationEvent : "keyup",
38611     /**
38612      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38613      */
38614     validateOnBlur : true,
38615     /**
38616      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38617      */
38618     validationDelay : 250,
38619     /**
38620      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38621      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38622      */
38623     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38624     /**
38625      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38626      */
38627     fieldClass : "x-form-field",
38628     /**
38629      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38630      *<pre>
38631 Value         Description
38632 -----------   ----------------------------------------------------------------------
38633 qtip          Display a quick tip when the user hovers over the field
38634 title         Display a default browser title attribute popup
38635 under         Add a block div beneath the field containing the error text
38636 side          Add an error icon to the right of the field with a popup on hover
38637 [element id]  Add the error text directly to the innerHTML of the specified element
38638 </pre>
38639      */
38640     msgTarget : 'qtip',
38641     /**
38642      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38643      */
38644     msgFx : 'normal',
38645
38646     /**
38647      * @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.
38648      */
38649     readOnly : false,
38650
38651     /**
38652      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38653      */
38654     disabled : false,
38655
38656     /**
38657      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38658      */
38659     inputType : undefined,
38660     
38661     /**
38662      * @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).
38663          */
38664         tabIndex : undefined,
38665         
38666     // private
38667     isFormField : true,
38668
38669     // private
38670     hasFocus : false,
38671     /**
38672      * @property {Roo.Element} fieldEl
38673      * Element Containing the rendered Field (with label etc.)
38674      */
38675     /**
38676      * @cfg {Mixed} value A value to initialize this field with.
38677      */
38678     value : undefined,
38679
38680     /**
38681      * @cfg {String} name The field's HTML name attribute.
38682      */
38683     /**
38684      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38685      */
38686     // private
38687     loadedValue : false,
38688      
38689      
38690         // private ??
38691         initComponent : function(){
38692         Roo.form.Field.superclass.initComponent.call(this);
38693         this.addEvents({
38694             /**
38695              * @event focus
38696              * Fires when this field receives input focus.
38697              * @param {Roo.form.Field} this
38698              */
38699             focus : true,
38700             /**
38701              * @event blur
38702              * Fires when this field loses input focus.
38703              * @param {Roo.form.Field} this
38704              */
38705             blur : true,
38706             /**
38707              * @event specialkey
38708              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38709              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38710              * @param {Roo.form.Field} this
38711              * @param {Roo.EventObject} e The event object
38712              */
38713             specialkey : true,
38714             /**
38715              * @event change
38716              * Fires just before the field blurs if the field value has changed.
38717              * @param {Roo.form.Field} this
38718              * @param {Mixed} newValue The new value
38719              * @param {Mixed} oldValue The original value
38720              */
38721             change : true,
38722             /**
38723              * @event invalid
38724              * Fires after the field has been marked as invalid.
38725              * @param {Roo.form.Field} this
38726              * @param {String} msg The validation message
38727              */
38728             invalid : true,
38729             /**
38730              * @event valid
38731              * Fires after the field has been validated with no errors.
38732              * @param {Roo.form.Field} this
38733              */
38734             valid : true,
38735              /**
38736              * @event keyup
38737              * Fires after the key up
38738              * @param {Roo.form.Field} this
38739              * @param {Roo.EventObject}  e The event Object
38740              */
38741             keyup : true
38742         });
38743     },
38744
38745     /**
38746      * Returns the name attribute of the field if available
38747      * @return {String} name The field name
38748      */
38749     getName: function(){
38750          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38751     },
38752
38753     // private
38754     onRender : function(ct, position){
38755         Roo.form.Field.superclass.onRender.call(this, ct, position);
38756         if(!this.el){
38757             var cfg = this.getAutoCreate();
38758             if(!cfg.name){
38759                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38760             }
38761             if (!cfg.name.length) {
38762                 delete cfg.name;
38763             }
38764             if(this.inputType){
38765                 cfg.type = this.inputType;
38766             }
38767             this.el = ct.createChild(cfg, position);
38768         }
38769         var type = this.el.dom.type;
38770         if(type){
38771             if(type == 'password'){
38772                 type = 'text';
38773             }
38774             this.el.addClass('x-form-'+type);
38775         }
38776         if(this.readOnly){
38777             this.el.dom.readOnly = true;
38778         }
38779         if(this.tabIndex !== undefined){
38780             this.el.dom.setAttribute('tabIndex', this.tabIndex);
38781         }
38782
38783         this.el.addClass([this.fieldClass, this.cls]);
38784         this.initValue();
38785     },
38786
38787     /**
38788      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
38789      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
38790      * @return {Roo.form.Field} this
38791      */
38792     applyTo : function(target){
38793         this.allowDomMove = false;
38794         this.el = Roo.get(target);
38795         this.render(this.el.dom.parentNode);
38796         return this;
38797     },
38798
38799     // private
38800     initValue : function(){
38801         if(this.value !== undefined){
38802             this.setValue(this.value);
38803         }else if(this.el.dom.value.length > 0){
38804             this.setValue(this.el.dom.value);
38805         }
38806     },
38807
38808     /**
38809      * Returns true if this field has been changed since it was originally loaded and is not disabled.
38810      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
38811      */
38812     isDirty : function() {
38813         if(this.disabled) {
38814             return false;
38815         }
38816         return String(this.getValue()) !== String(this.originalValue);
38817     },
38818
38819     /**
38820      * stores the current value in loadedValue
38821      */
38822     resetHasChanged : function()
38823     {
38824         this.loadedValue = String(this.getValue());
38825     },
38826     /**
38827      * checks the current value against the 'loaded' value.
38828      * Note - will return false if 'resetHasChanged' has not been called first.
38829      */
38830     hasChanged : function()
38831     {
38832         if(this.disabled || this.readOnly) {
38833             return false;
38834         }
38835         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
38836     },
38837     
38838     
38839     
38840     // private
38841     afterRender : function(){
38842         Roo.form.Field.superclass.afterRender.call(this);
38843         this.initEvents();
38844     },
38845
38846     // private
38847     fireKey : function(e){
38848         //Roo.log('field ' + e.getKey());
38849         if(e.isNavKeyPress()){
38850             this.fireEvent("specialkey", this, e);
38851         }
38852     },
38853
38854     /**
38855      * Resets the current field value to the originally loaded value and clears any validation messages
38856      */
38857     reset : function(){
38858         this.setValue(this.resetValue);
38859         this.clearInvalid();
38860     },
38861
38862     // private
38863     initEvents : function(){
38864         // safari killled keypress - so keydown is now used..
38865         this.el.on("keydown" , this.fireKey,  this);
38866         this.el.on("focus", this.onFocus,  this);
38867         this.el.on("blur", this.onBlur,  this);
38868         this.el.relayEvent('keyup', this);
38869
38870         // reference to original value for reset
38871         this.originalValue = this.getValue();
38872         this.resetValue =  this.getValue();
38873     },
38874
38875     // private
38876     onFocus : function(){
38877         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38878             this.el.addClass(this.focusClass);
38879         }
38880         if(!this.hasFocus){
38881             this.hasFocus = true;
38882             this.startValue = this.getValue();
38883             this.fireEvent("focus", this);
38884         }
38885     },
38886
38887     beforeBlur : Roo.emptyFn,
38888
38889     // private
38890     onBlur : function(){
38891         this.beforeBlur();
38892         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
38893             this.el.removeClass(this.focusClass);
38894         }
38895         this.hasFocus = false;
38896         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
38897             this.validate();
38898         }
38899         var v = this.getValue();
38900         if(String(v) !== String(this.startValue)){
38901             this.fireEvent('change', this, v, this.startValue);
38902         }
38903         this.fireEvent("blur", this);
38904     },
38905
38906     /**
38907      * Returns whether or not the field value is currently valid
38908      * @param {Boolean} preventMark True to disable marking the field invalid
38909      * @return {Boolean} True if the value is valid, else false
38910      */
38911     isValid : function(preventMark){
38912         if(this.disabled){
38913             return true;
38914         }
38915         var restore = this.preventMark;
38916         this.preventMark = preventMark === true;
38917         var v = this.validateValue(this.processValue(this.getRawValue()));
38918         this.preventMark = restore;
38919         return v;
38920     },
38921
38922     /**
38923      * Validates the field value
38924      * @return {Boolean} True if the value is valid, else false
38925      */
38926     validate : function(){
38927         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
38928             this.clearInvalid();
38929             return true;
38930         }
38931         return false;
38932     },
38933
38934     processValue : function(value){
38935         return value;
38936     },
38937
38938     // private
38939     // Subclasses should provide the validation implementation by overriding this
38940     validateValue : function(value){
38941         return true;
38942     },
38943
38944     /**
38945      * Mark this field as invalid
38946      * @param {String} msg The validation message
38947      */
38948     markInvalid : function(msg){
38949         if(!this.rendered || this.preventMark){ // not rendered
38950             return;
38951         }
38952         
38953         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
38954         
38955         obj.el.addClass(this.invalidClass);
38956         msg = msg || this.invalidText;
38957         switch(this.msgTarget){
38958             case 'qtip':
38959                 obj.el.dom.qtip = msg;
38960                 obj.el.dom.qclass = 'x-form-invalid-tip';
38961                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
38962                     Roo.QuickTips.enable();
38963                 }
38964                 break;
38965             case 'title':
38966                 this.el.dom.title = msg;
38967                 break;
38968             case 'under':
38969                 if(!this.errorEl){
38970                     var elp = this.el.findParent('.x-form-element', 5, true);
38971                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
38972                     this.errorEl.setWidth(elp.getWidth(true)-20);
38973                 }
38974                 this.errorEl.update(msg);
38975                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
38976                 break;
38977             case 'side':
38978                 if(!this.errorIcon){
38979                     var elp = this.el.findParent('.x-form-element', 5, true);
38980                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
38981                 }
38982                 this.alignErrorIcon();
38983                 this.errorIcon.dom.qtip = msg;
38984                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
38985                 this.errorIcon.show();
38986                 this.on('resize', this.alignErrorIcon, this);
38987                 break;
38988             default:
38989                 var t = Roo.getDom(this.msgTarget);
38990                 t.innerHTML = msg;
38991                 t.style.display = this.msgDisplay;
38992                 break;
38993         }
38994         this.fireEvent('invalid', this, msg);
38995     },
38996
38997     // private
38998     alignErrorIcon : function(){
38999         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39000     },
39001
39002     /**
39003      * Clear any invalid styles/messages for this field
39004      */
39005     clearInvalid : function(){
39006         if(!this.rendered || this.preventMark){ // not rendered
39007             return;
39008         }
39009         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39010         
39011         obj.el.removeClass(this.invalidClass);
39012         switch(this.msgTarget){
39013             case 'qtip':
39014                 obj.el.dom.qtip = '';
39015                 break;
39016             case 'title':
39017                 this.el.dom.title = '';
39018                 break;
39019             case 'under':
39020                 if(this.errorEl){
39021                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39022                 }
39023                 break;
39024             case 'side':
39025                 if(this.errorIcon){
39026                     this.errorIcon.dom.qtip = '';
39027                     this.errorIcon.hide();
39028                     this.un('resize', this.alignErrorIcon, this);
39029                 }
39030                 break;
39031             default:
39032                 var t = Roo.getDom(this.msgTarget);
39033                 t.innerHTML = '';
39034                 t.style.display = 'none';
39035                 break;
39036         }
39037         this.fireEvent('valid', this);
39038     },
39039
39040     /**
39041      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39042      * @return {Mixed} value The field value
39043      */
39044     getRawValue : function(){
39045         var v = this.el.getValue();
39046         
39047         return v;
39048     },
39049
39050     /**
39051      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39052      * @return {Mixed} value The field value
39053      */
39054     getValue : function(){
39055         var v = this.el.getValue();
39056          
39057         return v;
39058     },
39059
39060     /**
39061      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39062      * @param {Mixed} value The value to set
39063      */
39064     setRawValue : function(v){
39065         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39066     },
39067
39068     /**
39069      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39070      * @param {Mixed} value The value to set
39071      */
39072     setValue : function(v){
39073         this.value = v;
39074         if(this.rendered){
39075             this.el.dom.value = (v === null || v === undefined ? '' : v);
39076              this.validate();
39077         }
39078     },
39079
39080     adjustSize : function(w, h){
39081         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39082         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39083         return s;
39084     },
39085
39086     adjustWidth : function(tag, w){
39087         tag = tag.toLowerCase();
39088         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39089             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39090                 if(tag == 'input'){
39091                     return w + 2;
39092                 }
39093                 if(tag == 'textarea'){
39094                     return w-2;
39095                 }
39096             }else if(Roo.isOpera){
39097                 if(tag == 'input'){
39098                     return w + 2;
39099                 }
39100                 if(tag == 'textarea'){
39101                     return w-2;
39102                 }
39103             }
39104         }
39105         return w;
39106     }
39107 });
39108
39109
39110 // anything other than normal should be considered experimental
39111 Roo.form.Field.msgFx = {
39112     normal : {
39113         show: function(msgEl, f){
39114             msgEl.setDisplayed('block');
39115         },
39116
39117         hide : function(msgEl, f){
39118             msgEl.setDisplayed(false).update('');
39119         }
39120     },
39121
39122     slide : {
39123         show: function(msgEl, f){
39124             msgEl.slideIn('t', {stopFx:true});
39125         },
39126
39127         hide : function(msgEl, f){
39128             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39129         }
39130     },
39131
39132     slideRight : {
39133         show: function(msgEl, f){
39134             msgEl.fixDisplay();
39135             msgEl.alignTo(f.el, 'tl-tr');
39136             msgEl.slideIn('l', {stopFx:true});
39137         },
39138
39139         hide : function(msgEl, f){
39140             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39141         }
39142     }
39143 };/*
39144  * Based on:
39145  * Ext JS Library 1.1.1
39146  * Copyright(c) 2006-2007, Ext JS, LLC.
39147  *
39148  * Originally Released Under LGPL - original licence link has changed is not relivant.
39149  *
39150  * Fork - LGPL
39151  * <script type="text/javascript">
39152  */
39153  
39154
39155 /**
39156  * @class Roo.form.TextField
39157  * @extends Roo.form.Field
39158  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39159  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39160  * @constructor
39161  * Creates a new TextField
39162  * @param {Object} config Configuration options
39163  */
39164 Roo.form.TextField = function(config){
39165     Roo.form.TextField.superclass.constructor.call(this, config);
39166     this.addEvents({
39167         /**
39168          * @event autosize
39169          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39170          * according to the default logic, but this event provides a hook for the developer to apply additional
39171          * logic at runtime to resize the field if needed.
39172              * @param {Roo.form.Field} this This text field
39173              * @param {Number} width The new field width
39174              */
39175         autosize : true
39176     });
39177 };
39178
39179 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39180     /**
39181      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39182      */
39183     grow : false,
39184     /**
39185      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39186      */
39187     growMin : 30,
39188     /**
39189      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39190      */
39191     growMax : 800,
39192     /**
39193      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39194      */
39195     vtype : null,
39196     /**
39197      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39198      */
39199     maskRe : null,
39200     /**
39201      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39202      */
39203     disableKeyFilter : false,
39204     /**
39205      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39206      */
39207     allowBlank : true,
39208     /**
39209      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39210      */
39211     minLength : 0,
39212     /**
39213      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39214      */
39215     maxLength : Number.MAX_VALUE,
39216     /**
39217      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39218      */
39219     minLengthText : "The minimum length for this field is {0}",
39220     /**
39221      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39222      */
39223     maxLengthText : "The maximum length for this field is {0}",
39224     /**
39225      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39226      */
39227     selectOnFocus : false,
39228     /**
39229      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39230      */
39231     blankText : "This field is required",
39232     /**
39233      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39234      * If available, this function will be called only after the basic validators all return true, and will be passed the
39235      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39236      */
39237     validator : null,
39238     /**
39239      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39240      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39241      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39242      */
39243     regex : null,
39244     /**
39245      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39246      */
39247     regexText : "",
39248     /**
39249      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39250      */
39251     emptyText : null,
39252    
39253
39254     // private
39255     initEvents : function()
39256     {
39257         if (this.emptyText) {
39258             this.el.attr('placeholder', this.emptyText);
39259         }
39260         
39261         Roo.form.TextField.superclass.initEvents.call(this);
39262         if(this.validationEvent == 'keyup'){
39263             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39264             this.el.on('keyup', this.filterValidation, this);
39265         }
39266         else if(this.validationEvent !== false){
39267             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39268         }
39269         
39270         if(this.selectOnFocus){
39271             this.on("focus", this.preFocus, this);
39272             
39273         }
39274         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39275             this.el.on("keypress", this.filterKeys, this);
39276         }
39277         if(this.grow){
39278             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39279             this.el.on("click", this.autoSize,  this);
39280         }
39281         if(this.el.is('input[type=password]') && Roo.isSafari){
39282             this.el.on('keydown', this.SafariOnKeyDown, this);
39283         }
39284     },
39285
39286     processValue : function(value){
39287         if(this.stripCharsRe){
39288             var newValue = value.replace(this.stripCharsRe, '');
39289             if(newValue !== value){
39290                 this.setRawValue(newValue);
39291                 return newValue;
39292             }
39293         }
39294         return value;
39295     },
39296
39297     filterValidation : function(e){
39298         if(!e.isNavKeyPress()){
39299             this.validationTask.delay(this.validationDelay);
39300         }
39301     },
39302
39303     // private
39304     onKeyUp : function(e){
39305         if(!e.isNavKeyPress()){
39306             this.autoSize();
39307         }
39308     },
39309
39310     /**
39311      * Resets the current field value to the originally-loaded value and clears any validation messages.
39312      *  
39313      */
39314     reset : function(){
39315         Roo.form.TextField.superclass.reset.call(this);
39316        
39317     },
39318
39319     
39320     // private
39321     preFocus : function(){
39322         
39323         if(this.selectOnFocus){
39324             this.el.dom.select();
39325         }
39326     },
39327
39328     
39329     // private
39330     filterKeys : function(e){
39331         var k = e.getKey();
39332         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39333             return;
39334         }
39335         var c = e.getCharCode(), cc = String.fromCharCode(c);
39336         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39337             return;
39338         }
39339         if(!this.maskRe.test(cc)){
39340             e.stopEvent();
39341         }
39342     },
39343
39344     setValue : function(v){
39345         
39346         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39347         
39348         this.autoSize();
39349     },
39350
39351     /**
39352      * Validates a value according to the field's validation rules and marks the field as invalid
39353      * if the validation fails
39354      * @param {Mixed} value The value to validate
39355      * @return {Boolean} True if the value is valid, else false
39356      */
39357     validateValue : function(value){
39358         if(value.length < 1)  { // if it's blank
39359              if(this.allowBlank){
39360                 this.clearInvalid();
39361                 return true;
39362              }else{
39363                 this.markInvalid(this.blankText);
39364                 return false;
39365              }
39366         }
39367         if(value.length < this.minLength){
39368             this.markInvalid(String.format(this.minLengthText, this.minLength));
39369             return false;
39370         }
39371         if(value.length > this.maxLength){
39372             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39373             return false;
39374         }
39375         if(this.vtype){
39376             var vt = Roo.form.VTypes;
39377             if(!vt[this.vtype](value, this)){
39378                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39379                 return false;
39380             }
39381         }
39382         if(typeof this.validator == "function"){
39383             var msg = this.validator(value);
39384             if(msg !== true){
39385                 this.markInvalid(msg);
39386                 return false;
39387             }
39388         }
39389         if(this.regex && !this.regex.test(value)){
39390             this.markInvalid(this.regexText);
39391             return false;
39392         }
39393         return true;
39394     },
39395
39396     /**
39397      * Selects text in this field
39398      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39399      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39400      */
39401     selectText : function(start, end){
39402         var v = this.getRawValue();
39403         if(v.length > 0){
39404             start = start === undefined ? 0 : start;
39405             end = end === undefined ? v.length : end;
39406             var d = this.el.dom;
39407             if(d.setSelectionRange){
39408                 d.setSelectionRange(start, end);
39409             }else if(d.createTextRange){
39410                 var range = d.createTextRange();
39411                 range.moveStart("character", start);
39412                 range.moveEnd("character", v.length-end);
39413                 range.select();
39414             }
39415         }
39416     },
39417
39418     /**
39419      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39420      * This only takes effect if grow = true, and fires the autosize event.
39421      */
39422     autoSize : function(){
39423         if(!this.grow || !this.rendered){
39424             return;
39425         }
39426         if(!this.metrics){
39427             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39428         }
39429         var el = this.el;
39430         var v = el.dom.value;
39431         var d = document.createElement('div');
39432         d.appendChild(document.createTextNode(v));
39433         v = d.innerHTML;
39434         d = null;
39435         v += "&#160;";
39436         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39437         this.el.setWidth(w);
39438         this.fireEvent("autosize", this, w);
39439     },
39440     
39441     // private
39442     SafariOnKeyDown : function(event)
39443     {
39444         // this is a workaround for a password hang bug on chrome/ webkit.
39445         
39446         var isSelectAll = false;
39447         
39448         if(this.el.dom.selectionEnd > 0){
39449             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39450         }
39451         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39452             event.preventDefault();
39453             this.setValue('');
39454             return;
39455         }
39456         
39457         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39458             
39459             event.preventDefault();
39460             // this is very hacky as keydown always get's upper case.
39461             
39462             var cc = String.fromCharCode(event.getCharCode());
39463             
39464             
39465             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39466             
39467         }
39468         
39469         
39470     }
39471 });/*
39472  * Based on:
39473  * Ext JS Library 1.1.1
39474  * Copyright(c) 2006-2007, Ext JS, LLC.
39475  *
39476  * Originally Released Under LGPL - original licence link has changed is not relivant.
39477  *
39478  * Fork - LGPL
39479  * <script type="text/javascript">
39480  */
39481  
39482 /**
39483  * @class Roo.form.Hidden
39484  * @extends Roo.form.TextField
39485  * Simple Hidden element used on forms 
39486  * 
39487  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39488  * 
39489  * @constructor
39490  * Creates a new Hidden form element.
39491  * @param {Object} config Configuration options
39492  */
39493
39494
39495
39496 // easy hidden field...
39497 Roo.form.Hidden = function(config){
39498     Roo.form.Hidden.superclass.constructor.call(this, config);
39499 };
39500   
39501 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39502     fieldLabel:      '',
39503     inputType:      'hidden',
39504     width:          50,
39505     allowBlank:     true,
39506     labelSeparator: '',
39507     hidden:         true,
39508     itemCls :       'x-form-item-display-none'
39509
39510
39511 });
39512
39513
39514 /*
39515  * Based on:
39516  * Ext JS Library 1.1.1
39517  * Copyright(c) 2006-2007, Ext JS, LLC.
39518  *
39519  * Originally Released Under LGPL - original licence link has changed is not relivant.
39520  *
39521  * Fork - LGPL
39522  * <script type="text/javascript">
39523  */
39524  
39525 /**
39526  * @class Roo.form.TriggerField
39527  * @extends Roo.form.TextField
39528  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39529  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39530  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39531  * for which you can provide a custom implementation.  For example:
39532  * <pre><code>
39533 var trigger = new Roo.form.TriggerField();
39534 trigger.onTriggerClick = myTriggerFn;
39535 trigger.applyTo('my-field');
39536 </code></pre>
39537  *
39538  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39539  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39540  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39541  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39542  * @constructor
39543  * Create a new TriggerField.
39544  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39545  * to the base TextField)
39546  */
39547 Roo.form.TriggerField = function(config){
39548     this.mimicing = false;
39549     Roo.form.TriggerField.superclass.constructor.call(this, config);
39550 };
39551
39552 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39553     /**
39554      * @cfg {String} triggerClass A CSS class to apply to the trigger
39555      */
39556     /**
39557      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39558      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39559      */
39560     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39561     /**
39562      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39563      */
39564     hideTrigger:false,
39565
39566     /** @cfg {Boolean} grow @hide */
39567     /** @cfg {Number} growMin @hide */
39568     /** @cfg {Number} growMax @hide */
39569
39570     /**
39571      * @hide 
39572      * @method
39573      */
39574     autoSize: Roo.emptyFn,
39575     // private
39576     monitorTab : true,
39577     // private
39578     deferHeight : true,
39579
39580     
39581     actionMode : 'wrap',
39582     // private
39583     onResize : function(w, h){
39584         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39585         if(typeof w == 'number'){
39586             var x = w - this.trigger.getWidth();
39587             this.el.setWidth(this.adjustWidth('input', x));
39588             this.trigger.setStyle('left', x+'px');
39589         }
39590     },
39591
39592     // private
39593     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39594
39595     // private
39596     getResizeEl : function(){
39597         return this.wrap;
39598     },
39599
39600     // private
39601     getPositionEl : function(){
39602         return this.wrap;
39603     },
39604
39605     // private
39606     alignErrorIcon : function(){
39607         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39608     },
39609
39610     // private
39611     onRender : function(ct, position){
39612         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39613         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39614         this.trigger = this.wrap.createChild(this.triggerConfig ||
39615                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39616         if(this.hideTrigger){
39617             this.trigger.setDisplayed(false);
39618         }
39619         this.initTrigger();
39620         if(!this.width){
39621             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39622         }
39623     },
39624
39625     // private
39626     initTrigger : function(){
39627         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39628         this.trigger.addClassOnOver('x-form-trigger-over');
39629         this.trigger.addClassOnClick('x-form-trigger-click');
39630     },
39631
39632     // private
39633     onDestroy : function(){
39634         if(this.trigger){
39635             this.trigger.removeAllListeners();
39636             this.trigger.remove();
39637         }
39638         if(this.wrap){
39639             this.wrap.remove();
39640         }
39641         Roo.form.TriggerField.superclass.onDestroy.call(this);
39642     },
39643
39644     // private
39645     onFocus : function(){
39646         Roo.form.TriggerField.superclass.onFocus.call(this);
39647         if(!this.mimicing){
39648             this.wrap.addClass('x-trigger-wrap-focus');
39649             this.mimicing = true;
39650             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39651             if(this.monitorTab){
39652                 this.el.on("keydown", this.checkTab, this);
39653             }
39654         }
39655     },
39656
39657     // private
39658     checkTab : function(e){
39659         if(e.getKey() == e.TAB){
39660             this.triggerBlur();
39661         }
39662     },
39663
39664     // private
39665     onBlur : function(){
39666         // do nothing
39667     },
39668
39669     // private
39670     mimicBlur : function(e, t){
39671         if(!this.wrap.contains(t) && this.validateBlur()){
39672             this.triggerBlur();
39673         }
39674     },
39675
39676     // private
39677     triggerBlur : function(){
39678         this.mimicing = false;
39679         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39680         if(this.monitorTab){
39681             this.el.un("keydown", this.checkTab, this);
39682         }
39683         this.wrap.removeClass('x-trigger-wrap-focus');
39684         Roo.form.TriggerField.superclass.onBlur.call(this);
39685     },
39686
39687     // private
39688     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39689     validateBlur : function(e, t){
39690         return true;
39691     },
39692
39693     // private
39694     onDisable : function(){
39695         Roo.form.TriggerField.superclass.onDisable.call(this);
39696         if(this.wrap){
39697             this.wrap.addClass('x-item-disabled');
39698         }
39699     },
39700
39701     // private
39702     onEnable : function(){
39703         Roo.form.TriggerField.superclass.onEnable.call(this);
39704         if(this.wrap){
39705             this.wrap.removeClass('x-item-disabled');
39706         }
39707     },
39708
39709     // private
39710     onShow : function(){
39711         var ae = this.getActionEl();
39712         
39713         if(ae){
39714             ae.dom.style.display = '';
39715             ae.dom.style.visibility = 'visible';
39716         }
39717     },
39718
39719     // private
39720     
39721     onHide : function(){
39722         var ae = this.getActionEl();
39723         ae.dom.style.display = 'none';
39724     },
39725
39726     /**
39727      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39728      * by an implementing function.
39729      * @method
39730      * @param {EventObject} e
39731      */
39732     onTriggerClick : Roo.emptyFn
39733 });
39734
39735 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39736 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39737 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39738 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39739     initComponent : function(){
39740         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39741
39742         this.triggerConfig = {
39743             tag:'span', cls:'x-form-twin-triggers', cn:[
39744             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39745             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39746         ]};
39747     },
39748
39749     getTrigger : function(index){
39750         return this.triggers[index];
39751     },
39752
39753     initTrigger : function(){
39754         var ts = this.trigger.select('.x-form-trigger', true);
39755         this.wrap.setStyle('overflow', 'hidden');
39756         var triggerField = this;
39757         ts.each(function(t, all, index){
39758             t.hide = function(){
39759                 var w = triggerField.wrap.getWidth();
39760                 this.dom.style.display = 'none';
39761                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39762             };
39763             t.show = function(){
39764                 var w = triggerField.wrap.getWidth();
39765                 this.dom.style.display = '';
39766                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
39767             };
39768             var triggerIndex = 'Trigger'+(index+1);
39769
39770             if(this['hide'+triggerIndex]){
39771                 t.dom.style.display = 'none';
39772             }
39773             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
39774             t.addClassOnOver('x-form-trigger-over');
39775             t.addClassOnClick('x-form-trigger-click');
39776         }, this);
39777         this.triggers = ts.elements;
39778     },
39779
39780     onTrigger1Click : Roo.emptyFn,
39781     onTrigger2Click : Roo.emptyFn
39782 });/*
39783  * Based on:
39784  * Ext JS Library 1.1.1
39785  * Copyright(c) 2006-2007, Ext JS, LLC.
39786  *
39787  * Originally Released Under LGPL - original licence link has changed is not relivant.
39788  *
39789  * Fork - LGPL
39790  * <script type="text/javascript">
39791  */
39792  
39793 /**
39794  * @class Roo.form.TextArea
39795  * @extends Roo.form.TextField
39796  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
39797  * support for auto-sizing.
39798  * @constructor
39799  * Creates a new TextArea
39800  * @param {Object} config Configuration options
39801  */
39802 Roo.form.TextArea = function(config){
39803     Roo.form.TextArea.superclass.constructor.call(this, config);
39804     // these are provided exchanges for backwards compat
39805     // minHeight/maxHeight were replaced by growMin/growMax to be
39806     // compatible with TextField growing config values
39807     if(this.minHeight !== undefined){
39808         this.growMin = this.minHeight;
39809     }
39810     if(this.maxHeight !== undefined){
39811         this.growMax = this.maxHeight;
39812     }
39813 };
39814
39815 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
39816     /**
39817      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
39818      */
39819     growMin : 60,
39820     /**
39821      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
39822      */
39823     growMax: 1000,
39824     /**
39825      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
39826      * in the field (equivalent to setting overflow: hidden, defaults to false)
39827      */
39828     preventScrollbars: false,
39829     /**
39830      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39831      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
39832      */
39833
39834     // private
39835     onRender : function(ct, position){
39836         if(!this.el){
39837             this.defaultAutoCreate = {
39838                 tag: "textarea",
39839                 style:"width:300px;height:60px;",
39840                 autocomplete: "new-password"
39841             };
39842         }
39843         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
39844         if(this.grow){
39845             this.textSizeEl = Roo.DomHelper.append(document.body, {
39846                 tag: "pre", cls: "x-form-grow-sizer"
39847             });
39848             if(this.preventScrollbars){
39849                 this.el.setStyle("overflow", "hidden");
39850             }
39851             this.el.setHeight(this.growMin);
39852         }
39853     },
39854
39855     onDestroy : function(){
39856         if(this.textSizeEl){
39857             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
39858         }
39859         Roo.form.TextArea.superclass.onDestroy.call(this);
39860     },
39861
39862     // private
39863     onKeyUp : function(e){
39864         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
39865             this.autoSize();
39866         }
39867     },
39868
39869     /**
39870      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
39871      * This only takes effect if grow = true, and fires the autosize event if the height changes.
39872      */
39873     autoSize : function(){
39874         if(!this.grow || !this.textSizeEl){
39875             return;
39876         }
39877         var el = this.el;
39878         var v = el.dom.value;
39879         var ts = this.textSizeEl;
39880
39881         ts.innerHTML = '';
39882         ts.appendChild(document.createTextNode(v));
39883         v = ts.innerHTML;
39884
39885         Roo.fly(ts).setWidth(this.el.getWidth());
39886         if(v.length < 1){
39887             v = "&#160;&#160;";
39888         }else{
39889             if(Roo.isIE){
39890                 v = v.replace(/\n/g, '<p>&#160;</p>');
39891             }
39892             v += "&#160;\n&#160;";
39893         }
39894         ts.innerHTML = v;
39895         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
39896         if(h != this.lastHeight){
39897             this.lastHeight = h;
39898             this.el.setHeight(h);
39899             this.fireEvent("autosize", this, h);
39900         }
39901     }
39902 });/*
39903  * Based on:
39904  * Ext JS Library 1.1.1
39905  * Copyright(c) 2006-2007, Ext JS, LLC.
39906  *
39907  * Originally Released Under LGPL - original licence link has changed is not relivant.
39908  *
39909  * Fork - LGPL
39910  * <script type="text/javascript">
39911  */
39912  
39913
39914 /**
39915  * @class Roo.form.NumberField
39916  * @extends Roo.form.TextField
39917  * Numeric text field that provides automatic keystroke filtering and numeric validation.
39918  * @constructor
39919  * Creates a new NumberField
39920  * @param {Object} config Configuration options
39921  */
39922 Roo.form.NumberField = function(config){
39923     Roo.form.NumberField.superclass.constructor.call(this, config);
39924 };
39925
39926 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
39927     /**
39928      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
39929      */
39930     fieldClass: "x-form-field x-form-num-field",
39931     /**
39932      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39933      */
39934     allowDecimals : true,
39935     /**
39936      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39937      */
39938     decimalSeparator : ".",
39939     /**
39940      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39941      */
39942     decimalPrecision : 2,
39943     /**
39944      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39945      */
39946     allowNegative : true,
39947     /**
39948      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39949      */
39950     minValue : Number.NEGATIVE_INFINITY,
39951     /**
39952      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39953      */
39954     maxValue : Number.MAX_VALUE,
39955     /**
39956      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39957      */
39958     minText : "The minimum value for this field is {0}",
39959     /**
39960      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39961      */
39962     maxText : "The maximum value for this field is {0}",
39963     /**
39964      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39965      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39966      */
39967     nanText : "{0} is not a valid number",
39968
39969     // private
39970     initEvents : function(){
39971         Roo.form.NumberField.superclass.initEvents.call(this);
39972         var allowed = "0123456789";
39973         if(this.allowDecimals){
39974             allowed += this.decimalSeparator;
39975         }
39976         if(this.allowNegative){
39977             allowed += "-";
39978         }
39979         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39980         var keyPress = function(e){
39981             var k = e.getKey();
39982             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39983                 return;
39984             }
39985             var c = e.getCharCode();
39986             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39987                 e.stopEvent();
39988             }
39989         };
39990         this.el.on("keypress", keyPress, this);
39991     },
39992
39993     // private
39994     validateValue : function(value){
39995         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
39996             return false;
39997         }
39998         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39999              return true;
40000         }
40001         var num = this.parseValue(value);
40002         if(isNaN(num)){
40003             this.markInvalid(String.format(this.nanText, value));
40004             return false;
40005         }
40006         if(num < this.minValue){
40007             this.markInvalid(String.format(this.minText, this.minValue));
40008             return false;
40009         }
40010         if(num > this.maxValue){
40011             this.markInvalid(String.format(this.maxText, this.maxValue));
40012             return false;
40013         }
40014         return true;
40015     },
40016
40017     getValue : function(){
40018         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40019     },
40020
40021     // private
40022     parseValue : function(value){
40023         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40024         return isNaN(value) ? '' : value;
40025     },
40026
40027     // private
40028     fixPrecision : function(value){
40029         var nan = isNaN(value);
40030         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40031             return nan ? '' : value;
40032         }
40033         return parseFloat(value).toFixed(this.decimalPrecision);
40034     },
40035
40036     setValue : function(v){
40037         v = this.fixPrecision(v);
40038         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40039     },
40040
40041     // private
40042     decimalPrecisionFcn : function(v){
40043         return Math.floor(v);
40044     },
40045
40046     beforeBlur : function(){
40047         var v = this.parseValue(this.getRawValue());
40048         if(v){
40049             this.setValue(v);
40050         }
40051     }
40052 });/*
40053  * Based on:
40054  * Ext JS Library 1.1.1
40055  * Copyright(c) 2006-2007, Ext JS, LLC.
40056  *
40057  * Originally Released Under LGPL - original licence link has changed is not relivant.
40058  *
40059  * Fork - LGPL
40060  * <script type="text/javascript">
40061  */
40062  
40063 /**
40064  * @class Roo.form.DateField
40065  * @extends Roo.form.TriggerField
40066  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40067 * @constructor
40068 * Create a new DateField
40069 * @param {Object} config
40070  */
40071 Roo.form.DateField = function(config){
40072     Roo.form.DateField.superclass.constructor.call(this, config);
40073     
40074       this.addEvents({
40075          
40076         /**
40077          * @event select
40078          * Fires when a date is selected
40079              * @param {Roo.form.DateField} combo This combo box
40080              * @param {Date} date The date selected
40081              */
40082         'select' : true
40083          
40084     });
40085     
40086     
40087     if(typeof this.minValue == "string") {
40088         this.minValue = this.parseDate(this.minValue);
40089     }
40090     if(typeof this.maxValue == "string") {
40091         this.maxValue = this.parseDate(this.maxValue);
40092     }
40093     this.ddMatch = null;
40094     if(this.disabledDates){
40095         var dd = this.disabledDates;
40096         var re = "(?:";
40097         for(var i = 0; i < dd.length; i++){
40098             re += dd[i];
40099             if(i != dd.length-1) {
40100                 re += "|";
40101             }
40102         }
40103         this.ddMatch = new RegExp(re + ")");
40104     }
40105 };
40106
40107 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40108     /**
40109      * @cfg {String} format
40110      * The default date format string which can be overriden for localization support.  The format must be
40111      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40112      */
40113     format : "m/d/y",
40114     /**
40115      * @cfg {String} altFormats
40116      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40117      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40118      */
40119     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40120     /**
40121      * @cfg {Array} disabledDays
40122      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40123      */
40124     disabledDays : null,
40125     /**
40126      * @cfg {String} disabledDaysText
40127      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40128      */
40129     disabledDaysText : "Disabled",
40130     /**
40131      * @cfg {Array} disabledDates
40132      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40133      * expression so they are very powerful. Some examples:
40134      * <ul>
40135      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40136      * <li>["03/08", "09/16"] would disable those days for every year</li>
40137      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40138      * <li>["03/../2006"] would disable every day in March 2006</li>
40139      * <li>["^03"] would disable every day in every March</li>
40140      * </ul>
40141      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40142      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40143      */
40144     disabledDates : null,
40145     /**
40146      * @cfg {String} disabledDatesText
40147      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40148      */
40149     disabledDatesText : "Disabled",
40150     /**
40151      * @cfg {Date/String} minValue
40152      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40153      * valid format (defaults to null).
40154      */
40155     minValue : null,
40156     /**
40157      * @cfg {Date/String} maxValue
40158      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40159      * valid format (defaults to null).
40160      */
40161     maxValue : null,
40162     /**
40163      * @cfg {String} minText
40164      * The error text to display when the date in the cell is before minValue (defaults to
40165      * 'The date in this field must be after {minValue}').
40166      */
40167     minText : "The date in this field must be equal to or after {0}",
40168     /**
40169      * @cfg {String} maxText
40170      * The error text to display when the date in the cell is after maxValue (defaults to
40171      * 'The date in this field must be before {maxValue}').
40172      */
40173     maxText : "The date in this field must be equal to or before {0}",
40174     /**
40175      * @cfg {String} invalidText
40176      * The error text to display when the date in the field is invalid (defaults to
40177      * '{value} is not a valid date - it must be in the format {format}').
40178      */
40179     invalidText : "{0} is not a valid date - it must be in the format {1}",
40180     /**
40181      * @cfg {String} triggerClass
40182      * An additional CSS class used to style the trigger button.  The trigger will always get the
40183      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40184      * which displays a calendar icon).
40185      */
40186     triggerClass : 'x-form-date-trigger',
40187     
40188
40189     /**
40190      * @cfg {Boolean} useIso
40191      * if enabled, then the date field will use a hidden field to store the 
40192      * real value as iso formated date. default (false)
40193      */ 
40194     useIso : false,
40195     /**
40196      * @cfg {String/Object} autoCreate
40197      * A DomHelper element spec, or true for a default element spec (defaults to
40198      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40199      */ 
40200     // private
40201     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40202     
40203     // private
40204     hiddenField: false,
40205     
40206     onRender : function(ct, position)
40207     {
40208         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40209         if (this.useIso) {
40210             //this.el.dom.removeAttribute('name'); 
40211             Roo.log("Changing name?");
40212             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40213             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40214                     'before', true);
40215             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40216             // prevent input submission
40217             this.hiddenName = this.name;
40218         }
40219             
40220             
40221     },
40222     
40223     // private
40224     validateValue : function(value)
40225     {
40226         value = this.formatDate(value);
40227         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40228             Roo.log('super failed');
40229             return false;
40230         }
40231         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40232              return true;
40233         }
40234         var svalue = value;
40235         value = this.parseDate(value);
40236         if(!value){
40237             Roo.log('parse date failed' + svalue);
40238             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40239             return false;
40240         }
40241         var time = value.getTime();
40242         if(this.minValue && time < this.minValue.getTime()){
40243             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40244             return false;
40245         }
40246         if(this.maxValue && time > this.maxValue.getTime()){
40247             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40248             return false;
40249         }
40250         if(this.disabledDays){
40251             var day = value.getDay();
40252             for(var i = 0; i < this.disabledDays.length; i++) {
40253                 if(day === this.disabledDays[i]){
40254                     this.markInvalid(this.disabledDaysText);
40255                     return false;
40256                 }
40257             }
40258         }
40259         var fvalue = this.formatDate(value);
40260         if(this.ddMatch && this.ddMatch.test(fvalue)){
40261             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40262             return false;
40263         }
40264         return true;
40265     },
40266
40267     // private
40268     // Provides logic to override the default TriggerField.validateBlur which just returns true
40269     validateBlur : function(){
40270         return !this.menu || !this.menu.isVisible();
40271     },
40272     
40273     getName: function()
40274     {
40275         // returns hidden if it's set..
40276         if (!this.rendered) {return ''};
40277         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40278         
40279     },
40280
40281     /**
40282      * Returns the current date value of the date field.
40283      * @return {Date} The date value
40284      */
40285     getValue : function(){
40286         
40287         return  this.hiddenField ?
40288                 this.hiddenField.value :
40289                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40290     },
40291
40292     /**
40293      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40294      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40295      * (the default format used is "m/d/y").
40296      * <br />Usage:
40297      * <pre><code>
40298 //All of these calls set the same date value (May 4, 2006)
40299
40300 //Pass a date object:
40301 var dt = new Date('5/4/06');
40302 dateField.setValue(dt);
40303
40304 //Pass a date string (default format):
40305 dateField.setValue('5/4/06');
40306
40307 //Pass a date string (custom format):
40308 dateField.format = 'Y-m-d';
40309 dateField.setValue('2006-5-4');
40310 </code></pre>
40311      * @param {String/Date} date The date or valid date string
40312      */
40313     setValue : function(date){
40314         if (this.hiddenField) {
40315             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40316         }
40317         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40318         // make sure the value field is always stored as a date..
40319         this.value = this.parseDate(date);
40320         
40321         
40322     },
40323
40324     // private
40325     parseDate : function(value){
40326         if(!value || value instanceof Date){
40327             return value;
40328         }
40329         var v = Date.parseDate(value, this.format);
40330          if (!v && this.useIso) {
40331             v = Date.parseDate(value, 'Y-m-d');
40332         }
40333         if(!v && this.altFormats){
40334             if(!this.altFormatsArray){
40335                 this.altFormatsArray = this.altFormats.split("|");
40336             }
40337             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40338                 v = Date.parseDate(value, this.altFormatsArray[i]);
40339             }
40340         }
40341         return v;
40342     },
40343
40344     // private
40345     formatDate : function(date, fmt){
40346         return (!date || !(date instanceof Date)) ?
40347                date : date.dateFormat(fmt || this.format);
40348     },
40349
40350     // private
40351     menuListeners : {
40352         select: function(m, d){
40353             
40354             this.setValue(d);
40355             this.fireEvent('select', this, d);
40356         },
40357         show : function(){ // retain focus styling
40358             this.onFocus();
40359         },
40360         hide : function(){
40361             this.focus.defer(10, this);
40362             var ml = this.menuListeners;
40363             this.menu.un("select", ml.select,  this);
40364             this.menu.un("show", ml.show,  this);
40365             this.menu.un("hide", ml.hide,  this);
40366         }
40367     },
40368
40369     // private
40370     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40371     onTriggerClick : function(){
40372         if(this.disabled){
40373             return;
40374         }
40375         if(this.menu == null){
40376             this.menu = new Roo.menu.DateMenu();
40377         }
40378         Roo.apply(this.menu.picker,  {
40379             showClear: this.allowBlank,
40380             minDate : this.minValue,
40381             maxDate : this.maxValue,
40382             disabledDatesRE : this.ddMatch,
40383             disabledDatesText : this.disabledDatesText,
40384             disabledDays : this.disabledDays,
40385             disabledDaysText : this.disabledDaysText,
40386             format : this.useIso ? 'Y-m-d' : this.format,
40387             minText : String.format(this.minText, this.formatDate(this.minValue)),
40388             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40389         });
40390         this.menu.on(Roo.apply({}, this.menuListeners, {
40391             scope:this
40392         }));
40393         this.menu.picker.setValue(this.getValue() || new Date());
40394         this.menu.show(this.el, "tl-bl?");
40395     },
40396
40397     beforeBlur : function(){
40398         var v = this.parseDate(this.getRawValue());
40399         if(v){
40400             this.setValue(v);
40401         }
40402     },
40403
40404     /*@
40405      * overide
40406      * 
40407      */
40408     isDirty : function() {
40409         if(this.disabled) {
40410             return false;
40411         }
40412         
40413         if(typeof(this.startValue) === 'undefined'){
40414             return false;
40415         }
40416         
40417         return String(this.getValue()) !== String(this.startValue);
40418         
40419     }
40420 });/*
40421  * Based on:
40422  * Ext JS Library 1.1.1
40423  * Copyright(c) 2006-2007, Ext JS, LLC.
40424  *
40425  * Originally Released Under LGPL - original licence link has changed is not relivant.
40426  *
40427  * Fork - LGPL
40428  * <script type="text/javascript">
40429  */
40430  
40431 /**
40432  * @class Roo.form.MonthField
40433  * @extends Roo.form.TriggerField
40434  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40435 * @constructor
40436 * Create a new MonthField
40437 * @param {Object} config
40438  */
40439 Roo.form.MonthField = function(config){
40440     
40441     Roo.form.MonthField.superclass.constructor.call(this, config);
40442     
40443       this.addEvents({
40444          
40445         /**
40446          * @event select
40447          * Fires when a date is selected
40448              * @param {Roo.form.MonthFieeld} combo This combo box
40449              * @param {Date} date The date selected
40450              */
40451         'select' : true
40452          
40453     });
40454     
40455     
40456     if(typeof this.minValue == "string") {
40457         this.minValue = this.parseDate(this.minValue);
40458     }
40459     if(typeof this.maxValue == "string") {
40460         this.maxValue = this.parseDate(this.maxValue);
40461     }
40462     this.ddMatch = null;
40463     if(this.disabledDates){
40464         var dd = this.disabledDates;
40465         var re = "(?:";
40466         for(var i = 0; i < dd.length; i++){
40467             re += dd[i];
40468             if(i != dd.length-1) {
40469                 re += "|";
40470             }
40471         }
40472         this.ddMatch = new RegExp(re + ")");
40473     }
40474 };
40475
40476 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40477     /**
40478      * @cfg {String} format
40479      * The default date format string which can be overriden for localization support.  The format must be
40480      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40481      */
40482     format : "M Y",
40483     /**
40484      * @cfg {String} altFormats
40485      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40486      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40487      */
40488     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40489     /**
40490      * @cfg {Array} disabledDays
40491      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40492      */
40493     disabledDays : [0,1,2,3,4,5,6],
40494     /**
40495      * @cfg {String} disabledDaysText
40496      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40497      */
40498     disabledDaysText : "Disabled",
40499     /**
40500      * @cfg {Array} disabledDates
40501      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40502      * expression so they are very powerful. Some examples:
40503      * <ul>
40504      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40505      * <li>["03/08", "09/16"] would disable those days for every year</li>
40506      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40507      * <li>["03/../2006"] would disable every day in March 2006</li>
40508      * <li>["^03"] would disable every day in every March</li>
40509      * </ul>
40510      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40511      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40512      */
40513     disabledDates : null,
40514     /**
40515      * @cfg {String} disabledDatesText
40516      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40517      */
40518     disabledDatesText : "Disabled",
40519     /**
40520      * @cfg {Date/String} minValue
40521      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40522      * valid format (defaults to null).
40523      */
40524     minValue : null,
40525     /**
40526      * @cfg {Date/String} maxValue
40527      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40528      * valid format (defaults to null).
40529      */
40530     maxValue : null,
40531     /**
40532      * @cfg {String} minText
40533      * The error text to display when the date in the cell is before minValue (defaults to
40534      * 'The date in this field must be after {minValue}').
40535      */
40536     minText : "The date in this field must be equal to or after {0}",
40537     /**
40538      * @cfg {String} maxTextf
40539      * The error text to display when the date in the cell is after maxValue (defaults to
40540      * 'The date in this field must be before {maxValue}').
40541      */
40542     maxText : "The date in this field must be equal to or before {0}",
40543     /**
40544      * @cfg {String} invalidText
40545      * The error text to display when the date in the field is invalid (defaults to
40546      * '{value} is not a valid date - it must be in the format {format}').
40547      */
40548     invalidText : "{0} is not a valid date - it must be in the format {1}",
40549     /**
40550      * @cfg {String} triggerClass
40551      * An additional CSS class used to style the trigger button.  The trigger will always get the
40552      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40553      * which displays a calendar icon).
40554      */
40555     triggerClass : 'x-form-date-trigger',
40556     
40557
40558     /**
40559      * @cfg {Boolean} useIso
40560      * if enabled, then the date field will use a hidden field to store the 
40561      * real value as iso formated date. default (true)
40562      */ 
40563     useIso : true,
40564     /**
40565      * @cfg {String/Object} autoCreate
40566      * A DomHelper element spec, or true for a default element spec (defaults to
40567      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40568      */ 
40569     // private
40570     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40571     
40572     // private
40573     hiddenField: false,
40574     
40575     hideMonthPicker : false,
40576     
40577     onRender : function(ct, position)
40578     {
40579         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40580         if (this.useIso) {
40581             this.el.dom.removeAttribute('name'); 
40582             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40583                     'before', true);
40584             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40585             // prevent input submission
40586             this.hiddenName = this.name;
40587         }
40588             
40589             
40590     },
40591     
40592     // private
40593     validateValue : function(value)
40594     {
40595         value = this.formatDate(value);
40596         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40597             return false;
40598         }
40599         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40600              return true;
40601         }
40602         var svalue = value;
40603         value = this.parseDate(value);
40604         if(!value){
40605             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40606             return false;
40607         }
40608         var time = value.getTime();
40609         if(this.minValue && time < this.minValue.getTime()){
40610             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40611             return false;
40612         }
40613         if(this.maxValue && time > this.maxValue.getTime()){
40614             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40615             return false;
40616         }
40617         /*if(this.disabledDays){
40618             var day = value.getDay();
40619             for(var i = 0; i < this.disabledDays.length; i++) {
40620                 if(day === this.disabledDays[i]){
40621                     this.markInvalid(this.disabledDaysText);
40622                     return false;
40623                 }
40624             }
40625         }
40626         */
40627         var fvalue = this.formatDate(value);
40628         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40629             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40630             return false;
40631         }
40632         */
40633         return true;
40634     },
40635
40636     // private
40637     // Provides logic to override the default TriggerField.validateBlur which just returns true
40638     validateBlur : function(){
40639         return !this.menu || !this.menu.isVisible();
40640     },
40641
40642     /**
40643      * Returns the current date value of the date field.
40644      * @return {Date} The date value
40645      */
40646     getValue : function(){
40647         
40648         
40649         
40650         return  this.hiddenField ?
40651                 this.hiddenField.value :
40652                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40653     },
40654
40655     /**
40656      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40657      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40658      * (the default format used is "m/d/y").
40659      * <br />Usage:
40660      * <pre><code>
40661 //All of these calls set the same date value (May 4, 2006)
40662
40663 //Pass a date object:
40664 var dt = new Date('5/4/06');
40665 monthField.setValue(dt);
40666
40667 //Pass a date string (default format):
40668 monthField.setValue('5/4/06');
40669
40670 //Pass a date string (custom format):
40671 monthField.format = 'Y-m-d';
40672 monthField.setValue('2006-5-4');
40673 </code></pre>
40674      * @param {String/Date} date The date or valid date string
40675      */
40676     setValue : function(date){
40677         Roo.log('month setValue' + date);
40678         // can only be first of month..
40679         
40680         var val = this.parseDate(date);
40681         
40682         if (this.hiddenField) {
40683             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40684         }
40685         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40686         this.value = this.parseDate(date);
40687     },
40688
40689     // private
40690     parseDate : function(value){
40691         if(!value || value instanceof Date){
40692             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40693             return value;
40694         }
40695         var v = Date.parseDate(value, this.format);
40696         if (!v && this.useIso) {
40697             v = Date.parseDate(value, 'Y-m-d');
40698         }
40699         if (v) {
40700             // 
40701             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40702         }
40703         
40704         
40705         if(!v && this.altFormats){
40706             if(!this.altFormatsArray){
40707                 this.altFormatsArray = this.altFormats.split("|");
40708             }
40709             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40710                 v = Date.parseDate(value, this.altFormatsArray[i]);
40711             }
40712         }
40713         return v;
40714     },
40715
40716     // private
40717     formatDate : function(date, fmt){
40718         return (!date || !(date instanceof Date)) ?
40719                date : date.dateFormat(fmt || this.format);
40720     },
40721
40722     // private
40723     menuListeners : {
40724         select: function(m, d){
40725             this.setValue(d);
40726             this.fireEvent('select', this, d);
40727         },
40728         show : function(){ // retain focus styling
40729             this.onFocus();
40730         },
40731         hide : function(){
40732             this.focus.defer(10, this);
40733             var ml = this.menuListeners;
40734             this.menu.un("select", ml.select,  this);
40735             this.menu.un("show", ml.show,  this);
40736             this.menu.un("hide", ml.hide,  this);
40737         }
40738     },
40739     // private
40740     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40741     onTriggerClick : function(){
40742         if(this.disabled){
40743             return;
40744         }
40745         if(this.menu == null){
40746             this.menu = new Roo.menu.DateMenu();
40747            
40748         }
40749         
40750         Roo.apply(this.menu.picker,  {
40751             
40752             showClear: this.allowBlank,
40753             minDate : this.minValue,
40754             maxDate : this.maxValue,
40755             disabledDatesRE : this.ddMatch,
40756             disabledDatesText : this.disabledDatesText,
40757             
40758             format : this.useIso ? 'Y-m-d' : this.format,
40759             minText : String.format(this.minText, this.formatDate(this.minValue)),
40760             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40761             
40762         });
40763          this.menu.on(Roo.apply({}, this.menuListeners, {
40764             scope:this
40765         }));
40766        
40767         
40768         var m = this.menu;
40769         var p = m.picker;
40770         
40771         // hide month picker get's called when we called by 'before hide';
40772         
40773         var ignorehide = true;
40774         p.hideMonthPicker  = function(disableAnim){
40775             if (ignorehide) {
40776                 return;
40777             }
40778              if(this.monthPicker){
40779                 Roo.log("hideMonthPicker called");
40780                 if(disableAnim === true){
40781                     this.monthPicker.hide();
40782                 }else{
40783                     this.monthPicker.slideOut('t', {duration:.2});
40784                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
40785                     p.fireEvent("select", this, this.value);
40786                     m.hide();
40787                 }
40788             }
40789         }
40790         
40791         Roo.log('picker set value');
40792         Roo.log(this.getValue());
40793         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
40794         m.show(this.el, 'tl-bl?');
40795         ignorehide  = false;
40796         // this will trigger hideMonthPicker..
40797         
40798         
40799         // hidden the day picker
40800         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
40801         
40802         
40803         
40804       
40805         
40806         p.showMonthPicker.defer(100, p);
40807     
40808         
40809        
40810     },
40811
40812     beforeBlur : function(){
40813         var v = this.parseDate(this.getRawValue());
40814         if(v){
40815             this.setValue(v);
40816         }
40817     }
40818
40819     /** @cfg {Boolean} grow @hide */
40820     /** @cfg {Number} growMin @hide */
40821     /** @cfg {Number} growMax @hide */
40822     /**
40823      * @hide
40824      * @method autoSize
40825      */
40826 });/*
40827  * Based on:
40828  * Ext JS Library 1.1.1
40829  * Copyright(c) 2006-2007, Ext JS, LLC.
40830  *
40831  * Originally Released Under LGPL - original licence link has changed is not relivant.
40832  *
40833  * Fork - LGPL
40834  * <script type="text/javascript">
40835  */
40836  
40837
40838 /**
40839  * @class Roo.form.ComboBox
40840  * @extends Roo.form.TriggerField
40841  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
40842  * @constructor
40843  * Create a new ComboBox.
40844  * @param {Object} config Configuration options
40845  */
40846 Roo.form.ComboBox = function(config){
40847     Roo.form.ComboBox.superclass.constructor.call(this, config);
40848     this.addEvents({
40849         /**
40850          * @event expand
40851          * Fires when the dropdown list is expanded
40852              * @param {Roo.form.ComboBox} combo This combo box
40853              */
40854         'expand' : true,
40855         /**
40856          * @event collapse
40857          * Fires when the dropdown list is collapsed
40858              * @param {Roo.form.ComboBox} combo This combo box
40859              */
40860         'collapse' : true,
40861         /**
40862          * @event beforeselect
40863          * Fires before a list item is selected. Return false to cancel the selection.
40864              * @param {Roo.form.ComboBox} combo This combo box
40865              * @param {Roo.data.Record} record The data record returned from the underlying store
40866              * @param {Number} index The index of the selected item in the dropdown list
40867              */
40868         'beforeselect' : true,
40869         /**
40870          * @event select
40871          * Fires when a list item is selected
40872              * @param {Roo.form.ComboBox} combo This combo box
40873              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
40874              * @param {Number} index The index of the selected item in the dropdown list
40875              */
40876         'select' : true,
40877         /**
40878          * @event beforequery
40879          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
40880          * The event object passed has these properties:
40881              * @param {Roo.form.ComboBox} combo This combo box
40882              * @param {String} query The query
40883              * @param {Boolean} forceAll true to force "all" query
40884              * @param {Boolean} cancel true to cancel the query
40885              * @param {Object} e The query event object
40886              */
40887         'beforequery': true,
40888          /**
40889          * @event add
40890          * Fires when the 'add' icon is pressed (add a listener to enable add button)
40891              * @param {Roo.form.ComboBox} combo This combo box
40892              */
40893         'add' : true,
40894         /**
40895          * @event edit
40896          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
40897              * @param {Roo.form.ComboBox} combo This combo box
40898              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
40899              */
40900         'edit' : true
40901         
40902         
40903     });
40904     if(this.transform){
40905         this.allowDomMove = false;
40906         var s = Roo.getDom(this.transform);
40907         if(!this.hiddenName){
40908             this.hiddenName = s.name;
40909         }
40910         if(!this.store){
40911             this.mode = 'local';
40912             var d = [], opts = s.options;
40913             for(var i = 0, len = opts.length;i < len; i++){
40914                 var o = opts[i];
40915                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
40916                 if(o.selected) {
40917                     this.value = value;
40918                 }
40919                 d.push([value, o.text]);
40920             }
40921             this.store = new Roo.data.SimpleStore({
40922                 'id': 0,
40923                 fields: ['value', 'text'],
40924                 data : d
40925             });
40926             this.valueField = 'value';
40927             this.displayField = 'text';
40928         }
40929         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
40930         if(!this.lazyRender){
40931             this.target = true;
40932             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
40933             s.parentNode.removeChild(s); // remove it
40934             this.render(this.el.parentNode);
40935         }else{
40936             s.parentNode.removeChild(s); // remove it
40937         }
40938
40939     }
40940     if (this.store) {
40941         this.store = Roo.factory(this.store, Roo.data);
40942     }
40943     
40944     this.selectedIndex = -1;
40945     if(this.mode == 'local'){
40946         if(config.queryDelay === undefined){
40947             this.queryDelay = 10;
40948         }
40949         if(config.minChars === undefined){
40950             this.minChars = 0;
40951         }
40952     }
40953 };
40954
40955 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
40956     /**
40957      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
40958      */
40959     /**
40960      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
40961      * rendering into an Roo.Editor, defaults to false)
40962      */
40963     /**
40964      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
40965      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
40966      */
40967     /**
40968      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
40969      */
40970     /**
40971      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
40972      * the dropdown list (defaults to undefined, with no header element)
40973      */
40974
40975      /**
40976      * @cfg {String/Roo.Template} tpl The template to use to render the output
40977      */
40978      
40979     // private
40980     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
40981     /**
40982      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
40983      */
40984     listWidth: undefined,
40985     /**
40986      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
40987      * mode = 'remote' or 'text' if mode = 'local')
40988      */
40989     displayField: undefined,
40990     /**
40991      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
40992      * mode = 'remote' or 'value' if mode = 'local'). 
40993      * Note: use of a valueField requires the user make a selection
40994      * in order for a value to be mapped.
40995      */
40996     valueField: undefined,
40997     
40998     
40999     /**
41000      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41001      * field's data value (defaults to the underlying DOM element's name)
41002      */
41003     hiddenName: undefined,
41004     /**
41005      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41006      */
41007     listClass: '',
41008     /**
41009      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41010      */
41011     selectedClass: 'x-combo-selected',
41012     /**
41013      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41014      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41015      * which displays a downward arrow icon).
41016      */
41017     triggerClass : 'x-form-arrow-trigger',
41018     /**
41019      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41020      */
41021     shadow:'sides',
41022     /**
41023      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41024      * anchor positions (defaults to 'tl-bl')
41025      */
41026     listAlign: 'tl-bl?',
41027     /**
41028      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41029      */
41030     maxHeight: 300,
41031     /**
41032      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41033      * query specified by the allQuery config option (defaults to 'query')
41034      */
41035     triggerAction: 'query',
41036     /**
41037      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41038      * (defaults to 4, does not apply if editable = false)
41039      */
41040     minChars : 4,
41041     /**
41042      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41043      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41044      */
41045     typeAhead: false,
41046     /**
41047      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41048      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41049      */
41050     queryDelay: 500,
41051     /**
41052      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41053      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41054      */
41055     pageSize: 0,
41056     /**
41057      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41058      * when editable = true (defaults to false)
41059      */
41060     selectOnFocus:false,
41061     /**
41062      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41063      */
41064     queryParam: 'query',
41065     /**
41066      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41067      * when mode = 'remote' (defaults to 'Loading...')
41068      */
41069     loadingText: 'Loading...',
41070     /**
41071      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41072      */
41073     resizable: false,
41074     /**
41075      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41076      */
41077     handleHeight : 8,
41078     /**
41079      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41080      * traditional select (defaults to true)
41081      */
41082     editable: true,
41083     /**
41084      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41085      */
41086     allQuery: '',
41087     /**
41088      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41089      */
41090     mode: 'remote',
41091     /**
41092      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41093      * listWidth has a higher value)
41094      */
41095     minListWidth : 70,
41096     /**
41097      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41098      * allow the user to set arbitrary text into the field (defaults to false)
41099      */
41100     forceSelection:false,
41101     /**
41102      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41103      * if typeAhead = true (defaults to 250)
41104      */
41105     typeAheadDelay : 250,
41106     /**
41107      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41108      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41109      */
41110     valueNotFoundText : undefined,
41111     /**
41112      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41113      */
41114     blockFocus : false,
41115     
41116     /**
41117      * @cfg {Boolean} disableClear Disable showing of clear button.
41118      */
41119     disableClear : false,
41120     /**
41121      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41122      */
41123     alwaysQuery : false,
41124     
41125     //private
41126     addicon : false,
41127     editicon: false,
41128     
41129     // element that contains real text value.. (when hidden is used..)
41130      
41131     // private
41132     onRender : function(ct, position){
41133         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41134         if(this.hiddenName){
41135             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41136                     'before', true);
41137             this.hiddenField.value =
41138                 this.hiddenValue !== undefined ? this.hiddenValue :
41139                 this.value !== undefined ? this.value : '';
41140
41141             // prevent input submission
41142             this.el.dom.removeAttribute('name');
41143              
41144              
41145         }
41146         if(Roo.isGecko){
41147             this.el.dom.setAttribute('autocomplete', 'off');
41148         }
41149
41150         var cls = 'x-combo-list';
41151
41152         this.list = new Roo.Layer({
41153             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41154         });
41155
41156         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41157         this.list.setWidth(lw);
41158         this.list.swallowEvent('mousewheel');
41159         this.assetHeight = 0;
41160
41161         if(this.title){
41162             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41163             this.assetHeight += this.header.getHeight();
41164         }
41165
41166         this.innerList = this.list.createChild({cls:cls+'-inner'});
41167         this.innerList.on('mouseover', this.onViewOver, this);
41168         this.innerList.on('mousemove', this.onViewMove, this);
41169         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41170         
41171         if(this.allowBlank && !this.pageSize && !this.disableClear){
41172             this.footer = this.list.createChild({cls:cls+'-ft'});
41173             this.pageTb = new Roo.Toolbar(this.footer);
41174            
41175         }
41176         if(this.pageSize){
41177             this.footer = this.list.createChild({cls:cls+'-ft'});
41178             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41179                     {pageSize: this.pageSize});
41180             
41181         }
41182         
41183         if (this.pageTb && this.allowBlank && !this.disableClear) {
41184             var _this = this;
41185             this.pageTb.add(new Roo.Toolbar.Fill(), {
41186                 cls: 'x-btn-icon x-btn-clear',
41187                 text: '&#160;',
41188                 handler: function()
41189                 {
41190                     _this.collapse();
41191                     _this.clearValue();
41192                     _this.onSelect(false, -1);
41193                 }
41194             });
41195         }
41196         if (this.footer) {
41197             this.assetHeight += this.footer.getHeight();
41198         }
41199         
41200
41201         if(!this.tpl){
41202             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41203         }
41204
41205         this.view = new Roo.View(this.innerList, this.tpl, {
41206             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41207         });
41208
41209         this.view.on('click', this.onViewClick, this);
41210
41211         this.store.on('beforeload', this.onBeforeLoad, this);
41212         this.store.on('load', this.onLoad, this);
41213         this.store.on('loadexception', this.onLoadException, this);
41214
41215         if(this.resizable){
41216             this.resizer = new Roo.Resizable(this.list,  {
41217                pinned:true, handles:'se'
41218             });
41219             this.resizer.on('resize', function(r, w, h){
41220                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41221                 this.listWidth = w;
41222                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41223                 this.restrictHeight();
41224             }, this);
41225             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41226         }
41227         if(!this.editable){
41228             this.editable = true;
41229             this.setEditable(false);
41230         }  
41231         
41232         
41233         if (typeof(this.events.add.listeners) != 'undefined') {
41234             
41235             this.addicon = this.wrap.createChild(
41236                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41237        
41238             this.addicon.on('click', function(e) {
41239                 this.fireEvent('add', this);
41240             }, this);
41241         }
41242         if (typeof(this.events.edit.listeners) != 'undefined') {
41243             
41244             this.editicon = this.wrap.createChild(
41245                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41246             if (this.addicon) {
41247                 this.editicon.setStyle('margin-left', '40px');
41248             }
41249             this.editicon.on('click', function(e) {
41250                 
41251                 // we fire even  if inothing is selected..
41252                 this.fireEvent('edit', this, this.lastData );
41253                 
41254             }, this);
41255         }
41256         
41257         
41258         
41259     },
41260
41261     // private
41262     initEvents : function(){
41263         Roo.form.ComboBox.superclass.initEvents.call(this);
41264
41265         this.keyNav = new Roo.KeyNav(this.el, {
41266             "up" : function(e){
41267                 this.inKeyMode = true;
41268                 this.selectPrev();
41269             },
41270
41271             "down" : function(e){
41272                 if(!this.isExpanded()){
41273                     this.onTriggerClick();
41274                 }else{
41275                     this.inKeyMode = true;
41276                     this.selectNext();
41277                 }
41278             },
41279
41280             "enter" : function(e){
41281                 this.onViewClick();
41282                 //return true;
41283             },
41284
41285             "esc" : function(e){
41286                 this.collapse();
41287             },
41288
41289             "tab" : function(e){
41290                 this.onViewClick(false);
41291                 this.fireEvent("specialkey", this, e);
41292                 return true;
41293             },
41294
41295             scope : this,
41296
41297             doRelay : function(foo, bar, hname){
41298                 if(hname == 'down' || this.scope.isExpanded()){
41299                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41300                 }
41301                 return true;
41302             },
41303
41304             forceKeyDown: true
41305         });
41306         this.queryDelay = Math.max(this.queryDelay || 10,
41307                 this.mode == 'local' ? 10 : 250);
41308         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41309         if(this.typeAhead){
41310             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41311         }
41312         if(this.editable !== false){
41313             this.el.on("keyup", this.onKeyUp, this);
41314         }
41315         if(this.forceSelection){
41316             this.on('blur', this.doForce, this);
41317         }
41318     },
41319
41320     onDestroy : function(){
41321         if(this.view){
41322             this.view.setStore(null);
41323             this.view.el.removeAllListeners();
41324             this.view.el.remove();
41325             this.view.purgeListeners();
41326         }
41327         if(this.list){
41328             this.list.destroy();
41329         }
41330         if(this.store){
41331             this.store.un('beforeload', this.onBeforeLoad, this);
41332             this.store.un('load', this.onLoad, this);
41333             this.store.un('loadexception', this.onLoadException, this);
41334         }
41335         Roo.form.ComboBox.superclass.onDestroy.call(this);
41336     },
41337
41338     // private
41339     fireKey : function(e){
41340         if(e.isNavKeyPress() && !this.list.isVisible()){
41341             this.fireEvent("specialkey", this, e);
41342         }
41343     },
41344
41345     // private
41346     onResize: function(w, h){
41347         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41348         
41349         if(typeof w != 'number'){
41350             // we do not handle it!?!?
41351             return;
41352         }
41353         var tw = this.trigger.getWidth();
41354         tw += this.addicon ? this.addicon.getWidth() : 0;
41355         tw += this.editicon ? this.editicon.getWidth() : 0;
41356         var x = w - tw;
41357         this.el.setWidth( this.adjustWidth('input', x));
41358             
41359         this.trigger.setStyle('left', x+'px');
41360         
41361         if(this.list && this.listWidth === undefined){
41362             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41363             this.list.setWidth(lw);
41364             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41365         }
41366         
41367     
41368         
41369     },
41370
41371     /**
41372      * Allow or prevent the user from directly editing the field text.  If false is passed,
41373      * the user will only be able to select from the items defined in the dropdown list.  This method
41374      * is the runtime equivalent of setting the 'editable' config option at config time.
41375      * @param {Boolean} value True to allow the user to directly edit the field text
41376      */
41377     setEditable : function(value){
41378         if(value == this.editable){
41379             return;
41380         }
41381         this.editable = value;
41382         if(!value){
41383             this.el.dom.setAttribute('readOnly', true);
41384             this.el.on('mousedown', this.onTriggerClick,  this);
41385             this.el.addClass('x-combo-noedit');
41386         }else{
41387             this.el.dom.setAttribute('readOnly', false);
41388             this.el.un('mousedown', this.onTriggerClick,  this);
41389             this.el.removeClass('x-combo-noedit');
41390         }
41391     },
41392
41393     // private
41394     onBeforeLoad : function(){
41395         if(!this.hasFocus){
41396             return;
41397         }
41398         this.innerList.update(this.loadingText ?
41399                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41400         this.restrictHeight();
41401         this.selectedIndex = -1;
41402     },
41403
41404     // private
41405     onLoad : function(){
41406         if(!this.hasFocus){
41407             return;
41408         }
41409         if(this.store.getCount() > 0){
41410             this.expand();
41411             this.restrictHeight();
41412             if(this.lastQuery == this.allQuery){
41413                 if(this.editable){
41414                     this.el.dom.select();
41415                 }
41416                 if(!this.selectByValue(this.value, true)){
41417                     this.select(0, true);
41418                 }
41419             }else{
41420                 this.selectNext();
41421                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41422                     this.taTask.delay(this.typeAheadDelay);
41423                 }
41424             }
41425         }else{
41426             this.onEmptyResults();
41427         }
41428         //this.el.focus();
41429     },
41430     // private
41431     onLoadException : function()
41432     {
41433         this.collapse();
41434         Roo.log(this.store.reader.jsonData);
41435         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41436             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41437         }
41438         
41439         
41440     },
41441     // private
41442     onTypeAhead : function(){
41443         if(this.store.getCount() > 0){
41444             var r = this.store.getAt(0);
41445             var newValue = r.data[this.displayField];
41446             var len = newValue.length;
41447             var selStart = this.getRawValue().length;
41448             if(selStart != len){
41449                 this.setRawValue(newValue);
41450                 this.selectText(selStart, newValue.length);
41451             }
41452         }
41453     },
41454
41455     // private
41456     onSelect : function(record, index){
41457         if(this.fireEvent('beforeselect', this, record, index) !== false){
41458             this.setFromData(index > -1 ? record.data : false);
41459             this.collapse();
41460             this.fireEvent('select', this, record, index);
41461         }
41462     },
41463
41464     /**
41465      * Returns the currently selected field value or empty string if no value is set.
41466      * @return {String} value The selected value
41467      */
41468     getValue : function(){
41469         if(this.valueField){
41470             return typeof this.value != 'undefined' ? this.value : '';
41471         }
41472         return Roo.form.ComboBox.superclass.getValue.call(this);
41473     },
41474
41475     /**
41476      * Clears any text/value currently set in the field
41477      */
41478     clearValue : function(){
41479         if(this.hiddenField){
41480             this.hiddenField.value = '';
41481         }
41482         this.value = '';
41483         this.setRawValue('');
41484         this.lastSelectionText = '';
41485         
41486     },
41487
41488     /**
41489      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41490      * will be displayed in the field.  If the value does not match the data value of an existing item,
41491      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41492      * Otherwise the field will be blank (although the value will still be set).
41493      * @param {String} value The value to match
41494      */
41495     setValue : function(v){
41496         var text = v;
41497         if(this.valueField){
41498             var r = this.findRecord(this.valueField, v);
41499             if(r){
41500                 text = r.data[this.displayField];
41501             }else if(this.valueNotFoundText !== undefined){
41502                 text = this.valueNotFoundText;
41503             }
41504         }
41505         this.lastSelectionText = text;
41506         if(this.hiddenField){
41507             this.hiddenField.value = v;
41508         }
41509         Roo.form.ComboBox.superclass.setValue.call(this, text);
41510         this.value = v;
41511     },
41512     /**
41513      * @property {Object} the last set data for the element
41514      */
41515     
41516     lastData : false,
41517     /**
41518      * Sets the value of the field based on a object which is related to the record format for the store.
41519      * @param {Object} value the value to set as. or false on reset?
41520      */
41521     setFromData : function(o){
41522         var dv = ''; // display value
41523         var vv = ''; // value value..
41524         this.lastData = o;
41525         if (this.displayField) {
41526             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41527         } else {
41528             // this is an error condition!!!
41529             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41530         }
41531         
41532         if(this.valueField){
41533             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41534         }
41535         if(this.hiddenField){
41536             this.hiddenField.value = vv;
41537             
41538             this.lastSelectionText = dv;
41539             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41540             this.value = vv;
41541             return;
41542         }
41543         // no hidden field.. - we store the value in 'value', but still display
41544         // display field!!!!
41545         this.lastSelectionText = dv;
41546         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41547         this.value = vv;
41548         
41549         
41550     },
41551     // private
41552     reset : function(){
41553         // overridden so that last data is reset..
41554         this.setValue(this.resetValue);
41555         this.clearInvalid();
41556         this.lastData = false;
41557         if (this.view) {
41558             this.view.clearSelections();
41559         }
41560     },
41561     // private
41562     findRecord : function(prop, value){
41563         var record;
41564         if(this.store.getCount() > 0){
41565             this.store.each(function(r){
41566                 if(r.data[prop] == value){
41567                     record = r;
41568                     return false;
41569                 }
41570                 return true;
41571             });
41572         }
41573         return record;
41574     },
41575     
41576     getName: function()
41577     {
41578         // returns hidden if it's set..
41579         if (!this.rendered) {return ''};
41580         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41581         
41582     },
41583     // private
41584     onViewMove : function(e, t){
41585         this.inKeyMode = false;
41586     },
41587
41588     // private
41589     onViewOver : function(e, t){
41590         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41591             return;
41592         }
41593         var item = this.view.findItemFromChild(t);
41594         if(item){
41595             var index = this.view.indexOf(item);
41596             this.select(index, false);
41597         }
41598     },
41599
41600     // private
41601     onViewClick : function(doFocus)
41602     {
41603         var index = this.view.getSelectedIndexes()[0];
41604         var r = this.store.getAt(index);
41605         if(r){
41606             this.onSelect(r, index);
41607         }
41608         if(doFocus !== false && !this.blockFocus){
41609             this.el.focus();
41610         }
41611     },
41612
41613     // private
41614     restrictHeight : function(){
41615         this.innerList.dom.style.height = '';
41616         var inner = this.innerList.dom;
41617         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41618         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41619         this.list.beginUpdate();
41620         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41621         this.list.alignTo(this.el, this.listAlign);
41622         this.list.endUpdate();
41623     },
41624
41625     // private
41626     onEmptyResults : function(){
41627         this.collapse();
41628     },
41629
41630     /**
41631      * Returns true if the dropdown list is expanded, else false.
41632      */
41633     isExpanded : function(){
41634         return this.list.isVisible();
41635     },
41636
41637     /**
41638      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41639      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41640      * @param {String} value The data value of the item to select
41641      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41642      * selected item if it is not currently in view (defaults to true)
41643      * @return {Boolean} True if the value matched an item in the list, else false
41644      */
41645     selectByValue : function(v, scrollIntoView){
41646         if(v !== undefined && v !== null){
41647             var r = this.findRecord(this.valueField || this.displayField, v);
41648             if(r){
41649                 this.select(this.store.indexOf(r), scrollIntoView);
41650                 return true;
41651             }
41652         }
41653         return false;
41654     },
41655
41656     /**
41657      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41658      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41659      * @param {Number} index The zero-based index of the list item to select
41660      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41661      * selected item if it is not currently in view (defaults to true)
41662      */
41663     select : function(index, scrollIntoView){
41664         this.selectedIndex = index;
41665         this.view.select(index);
41666         if(scrollIntoView !== false){
41667             var el = this.view.getNode(index);
41668             if(el){
41669                 this.innerList.scrollChildIntoView(el, false);
41670             }
41671         }
41672     },
41673
41674     // private
41675     selectNext : function(){
41676         var ct = this.store.getCount();
41677         if(ct > 0){
41678             if(this.selectedIndex == -1){
41679                 this.select(0);
41680             }else if(this.selectedIndex < ct-1){
41681                 this.select(this.selectedIndex+1);
41682             }
41683         }
41684     },
41685
41686     // private
41687     selectPrev : function(){
41688         var ct = this.store.getCount();
41689         if(ct > 0){
41690             if(this.selectedIndex == -1){
41691                 this.select(0);
41692             }else if(this.selectedIndex != 0){
41693                 this.select(this.selectedIndex-1);
41694             }
41695         }
41696     },
41697
41698     // private
41699     onKeyUp : function(e){
41700         if(this.editable !== false && !e.isSpecialKey()){
41701             this.lastKey = e.getKey();
41702             this.dqTask.delay(this.queryDelay);
41703         }
41704     },
41705
41706     // private
41707     validateBlur : function(){
41708         return !this.list || !this.list.isVisible();   
41709     },
41710
41711     // private
41712     initQuery : function(){
41713         this.doQuery(this.getRawValue());
41714     },
41715
41716     // private
41717     doForce : function(){
41718         if(this.el.dom.value.length > 0){
41719             this.el.dom.value =
41720                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41721              
41722         }
41723     },
41724
41725     /**
41726      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41727      * query allowing the query action to be canceled if needed.
41728      * @param {String} query The SQL query to execute
41729      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41730      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41731      * saved in the current store (defaults to false)
41732      */
41733     doQuery : function(q, forceAll){
41734         if(q === undefined || q === null){
41735             q = '';
41736         }
41737         var qe = {
41738             query: q,
41739             forceAll: forceAll,
41740             combo: this,
41741             cancel:false
41742         };
41743         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
41744             return false;
41745         }
41746         q = qe.query;
41747         forceAll = qe.forceAll;
41748         if(forceAll === true || (q.length >= this.minChars)){
41749             if(this.lastQuery != q || this.alwaysQuery){
41750                 this.lastQuery = q;
41751                 if(this.mode == 'local'){
41752                     this.selectedIndex = -1;
41753                     if(forceAll){
41754                         this.store.clearFilter();
41755                     }else{
41756                         this.store.filter(this.displayField, q);
41757                     }
41758                     this.onLoad();
41759                 }else{
41760                     this.store.baseParams[this.queryParam] = q;
41761                     this.store.load({
41762                         params: this.getParams(q)
41763                     });
41764                     this.expand();
41765                 }
41766             }else{
41767                 this.selectedIndex = -1;
41768                 this.onLoad();   
41769             }
41770         }
41771     },
41772
41773     // private
41774     getParams : function(q){
41775         var p = {};
41776         //p[this.queryParam] = q;
41777         if(this.pageSize){
41778             p.start = 0;
41779             p.limit = this.pageSize;
41780         }
41781         return p;
41782     },
41783
41784     /**
41785      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
41786      */
41787     collapse : function(){
41788         if(!this.isExpanded()){
41789             return;
41790         }
41791         this.list.hide();
41792         Roo.get(document).un('mousedown', this.collapseIf, this);
41793         Roo.get(document).un('mousewheel', this.collapseIf, this);
41794         if (!this.editable) {
41795             Roo.get(document).un('keydown', this.listKeyPress, this);
41796         }
41797         this.fireEvent('collapse', this);
41798     },
41799
41800     // private
41801     collapseIf : function(e){
41802         if(!e.within(this.wrap) && !e.within(this.list)){
41803             this.collapse();
41804         }
41805     },
41806
41807     /**
41808      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
41809      */
41810     expand : function(){
41811         if(this.isExpanded() || !this.hasFocus){
41812             return;
41813         }
41814         this.list.alignTo(this.el, this.listAlign);
41815         this.list.show();
41816         Roo.get(document).on('mousedown', this.collapseIf, this);
41817         Roo.get(document).on('mousewheel', this.collapseIf, this);
41818         if (!this.editable) {
41819             Roo.get(document).on('keydown', this.listKeyPress, this);
41820         }
41821         
41822         this.fireEvent('expand', this);
41823     },
41824
41825     // private
41826     // Implements the default empty TriggerField.onTriggerClick function
41827     onTriggerClick : function(){
41828         if(this.disabled){
41829             return;
41830         }
41831         if(this.isExpanded()){
41832             this.collapse();
41833             if (!this.blockFocus) {
41834                 this.el.focus();
41835             }
41836             
41837         }else {
41838             this.hasFocus = true;
41839             if(this.triggerAction == 'all') {
41840                 this.doQuery(this.allQuery, true);
41841             } else {
41842                 this.doQuery(this.getRawValue());
41843             }
41844             if (!this.blockFocus) {
41845                 this.el.focus();
41846             }
41847         }
41848     },
41849     listKeyPress : function(e)
41850     {
41851         //Roo.log('listkeypress');
41852         // scroll to first matching element based on key pres..
41853         if (e.isSpecialKey()) {
41854             return false;
41855         }
41856         var k = String.fromCharCode(e.getKey()).toUpperCase();
41857         //Roo.log(k);
41858         var match  = false;
41859         var csel = this.view.getSelectedNodes();
41860         var cselitem = false;
41861         if (csel.length) {
41862             var ix = this.view.indexOf(csel[0]);
41863             cselitem  = this.store.getAt(ix);
41864             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
41865                 cselitem = false;
41866             }
41867             
41868         }
41869         
41870         this.store.each(function(v) { 
41871             if (cselitem) {
41872                 // start at existing selection.
41873                 if (cselitem.id == v.id) {
41874                     cselitem = false;
41875                 }
41876                 return;
41877             }
41878                 
41879             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
41880                 match = this.store.indexOf(v);
41881                 return false;
41882             }
41883         }, this);
41884         
41885         if (match === false) {
41886             return true; // no more action?
41887         }
41888         // scroll to?
41889         this.view.select(match);
41890         var sn = Roo.get(this.view.getSelectedNodes()[0]);
41891         sn.scrollIntoView(sn.dom.parentNode, false);
41892     }
41893
41894     /** 
41895     * @cfg {Boolean} grow 
41896     * @hide 
41897     */
41898     /** 
41899     * @cfg {Number} growMin 
41900     * @hide 
41901     */
41902     /** 
41903     * @cfg {Number} growMax 
41904     * @hide 
41905     */
41906     /**
41907      * @hide
41908      * @method autoSize
41909      */
41910 });/*
41911  * Copyright(c) 2010-2012, Roo J Solutions Limited
41912  *
41913  * Licence LGPL
41914  *
41915  */
41916
41917 /**
41918  * @class Roo.form.ComboBoxArray
41919  * @extends Roo.form.TextField
41920  * A facebook style adder... for lists of email / people / countries  etc...
41921  * pick multiple items from a combo box, and shows each one.
41922  *
41923  *  Fred [x]  Brian [x]  [Pick another |v]
41924  *
41925  *
41926  *  For this to work: it needs various extra information
41927  *    - normal combo problay has
41928  *      name, hiddenName
41929  *    + displayField, valueField
41930  *
41931  *    For our purpose...
41932  *
41933  *
41934  *   If we change from 'extends' to wrapping...
41935  *   
41936  *  
41937  *
41938  
41939  
41940  * @constructor
41941  * Create a new ComboBoxArray.
41942  * @param {Object} config Configuration options
41943  */
41944  
41945
41946 Roo.form.ComboBoxArray = function(config)
41947 {
41948     this.addEvents({
41949         /**
41950          * @event beforeremove
41951          * Fires before remove the value from the list
41952              * @param {Roo.form.ComboBoxArray} _self This combo box array
41953              * @param {Roo.form.ComboBoxArray.Item} item removed item
41954              */
41955         'beforeremove' : true,
41956         /**
41957          * @event remove
41958          * Fires when remove the value from the list
41959              * @param {Roo.form.ComboBoxArray} _self This combo box array
41960              * @param {Roo.form.ComboBoxArray.Item} item removed item
41961              */
41962         'remove' : true
41963         
41964         
41965     });
41966     
41967     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
41968     
41969     this.items = new Roo.util.MixedCollection(false);
41970     
41971     // construct the child combo...
41972     
41973     
41974     
41975     
41976    
41977     
41978 }
41979
41980  
41981 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
41982
41983     /**
41984      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
41985      */
41986     
41987     lastData : false,
41988     
41989     // behavies liek a hiddne field
41990     inputType:      'hidden',
41991     /**
41992      * @cfg {Number} width The width of the box that displays the selected element
41993      */ 
41994     width:          300,
41995
41996     
41997     
41998     /**
41999      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42000      */
42001     name : false,
42002     /**
42003      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42004      */
42005     hiddenName : false,
42006     
42007     
42008     // private the array of items that are displayed..
42009     items  : false,
42010     // private - the hidden field el.
42011     hiddenEl : false,
42012     // private - the filed el..
42013     el : false,
42014     
42015     //validateValue : function() { return true; }, // all values are ok!
42016     //onAddClick: function() { },
42017     
42018     onRender : function(ct, position) 
42019     {
42020         
42021         // create the standard hidden element
42022         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42023         
42024         
42025         // give fake names to child combo;
42026         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42027         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
42028         
42029         this.combo = Roo.factory(this.combo, Roo.form);
42030         this.combo.onRender(ct, position);
42031         if (typeof(this.combo.width) != 'undefined') {
42032             this.combo.onResize(this.combo.width,0);
42033         }
42034         
42035         this.combo.initEvents();
42036         
42037         // assigned so form know we need to do this..
42038         this.store          = this.combo.store;
42039         this.valueField     = this.combo.valueField;
42040         this.displayField   = this.combo.displayField ;
42041         
42042         
42043         this.combo.wrap.addClass('x-cbarray-grp');
42044         
42045         var cbwrap = this.combo.wrap.createChild(
42046             {tag: 'div', cls: 'x-cbarray-cb'},
42047             this.combo.el.dom
42048         );
42049         
42050              
42051         this.hiddenEl = this.combo.wrap.createChild({
42052             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42053         });
42054         this.el = this.combo.wrap.createChild({
42055             tag: 'input',  type:'hidden' , name: this.name, value : ''
42056         });
42057          //   this.el.dom.removeAttribute("name");
42058         
42059         
42060         this.outerWrap = this.combo.wrap;
42061         this.wrap = cbwrap;
42062         
42063         this.outerWrap.setWidth(this.width);
42064         this.outerWrap.dom.removeChild(this.el.dom);
42065         
42066         this.wrap.dom.appendChild(this.el.dom);
42067         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42068         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42069         
42070         this.combo.trigger.setStyle('position','relative');
42071         this.combo.trigger.setStyle('left', '0px');
42072         this.combo.trigger.setStyle('top', '2px');
42073         
42074         this.combo.el.setStyle('vertical-align', 'text-bottom');
42075         
42076         //this.trigger.setStyle('vertical-align', 'top');
42077         
42078         // this should use the code from combo really... on('add' ....)
42079         if (this.adder) {
42080             
42081         
42082             this.adder = this.outerWrap.createChild(
42083                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42084             var _t = this;
42085             this.adder.on('click', function(e) {
42086                 _t.fireEvent('adderclick', this, e);
42087             }, _t);
42088         }
42089         //var _t = this;
42090         //this.adder.on('click', this.onAddClick, _t);
42091         
42092         
42093         this.combo.on('select', function(cb, rec, ix) {
42094             this.addItem(rec.data);
42095             
42096             cb.setValue('');
42097             cb.el.dom.value = '';
42098             //cb.lastData = rec.data;
42099             // add to list
42100             
42101         }, this);
42102         
42103         
42104     },
42105     
42106     
42107     getName: function()
42108     {
42109         // returns hidden if it's set..
42110         if (!this.rendered) {return ''};
42111         return  this.hiddenName ? this.hiddenName : this.name;
42112         
42113     },
42114     
42115     
42116     onResize: function(w, h){
42117         
42118         return;
42119         // not sure if this is needed..
42120         //this.combo.onResize(w,h);
42121         
42122         if(typeof w != 'number'){
42123             // we do not handle it!?!?
42124             return;
42125         }
42126         var tw = this.combo.trigger.getWidth();
42127         tw += this.addicon ? this.addicon.getWidth() : 0;
42128         tw += this.editicon ? this.editicon.getWidth() : 0;
42129         var x = w - tw;
42130         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42131             
42132         this.combo.trigger.setStyle('left', '0px');
42133         
42134         if(this.list && this.listWidth === undefined){
42135             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42136             this.list.setWidth(lw);
42137             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42138         }
42139         
42140     
42141         
42142     },
42143     
42144     addItem: function(rec)
42145     {
42146         var valueField = this.combo.valueField;
42147         var displayField = this.combo.displayField;
42148         if (this.items.indexOfKey(rec[valueField]) > -1) {
42149             //console.log("GOT " + rec.data.id);
42150             return;
42151         }
42152         
42153         var x = new Roo.form.ComboBoxArray.Item({
42154             //id : rec[this.idField],
42155             data : rec,
42156             displayField : displayField ,
42157             tipField : displayField ,
42158             cb : this
42159         });
42160         // use the 
42161         this.items.add(rec[valueField],x);
42162         // add it before the element..
42163         this.updateHiddenEl();
42164         x.render(this.outerWrap, this.wrap.dom);
42165         // add the image handler..
42166     },
42167     
42168     updateHiddenEl : function()
42169     {
42170         this.validate();
42171         if (!this.hiddenEl) {
42172             return;
42173         }
42174         var ar = [];
42175         var idField = this.combo.valueField;
42176         
42177         this.items.each(function(f) {
42178             ar.push(f.data[idField]);
42179            
42180         });
42181         this.hiddenEl.dom.value = ar.join(',');
42182         this.validate();
42183     },
42184     
42185     reset : function()
42186     {
42187         this.items.clear();
42188         
42189         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42190            el.remove();
42191         });
42192         
42193         this.el.dom.value = '';
42194         if (this.hiddenEl) {
42195             this.hiddenEl.dom.value = '';
42196         }
42197         
42198     },
42199     getValue: function()
42200     {
42201         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42202     },
42203     setValue: function(v) // not a valid action - must use addItems..
42204     {
42205          
42206         this.reset();
42207         
42208         
42209         
42210         if (this.store.isLocal && (typeof(v) == 'string')) {
42211             // then we can use the store to find the values..
42212             // comma seperated at present.. this needs to allow JSON based encoding..
42213             this.hiddenEl.value  = v;
42214             var v_ar = [];
42215             Roo.each(v.split(','), function(k) {
42216                 Roo.log("CHECK " + this.valueField + ',' + k);
42217                 var li = this.store.query(this.valueField, k);
42218                 if (!li.length) {
42219                     return;
42220                 }
42221                 var add = {};
42222                 add[this.valueField] = k;
42223                 add[this.displayField] = li.item(0).data[this.displayField];
42224                 
42225                 this.addItem(add);
42226             }, this) 
42227              
42228         }
42229         if (typeof(v) == 'object' ) {
42230             // then let's assume it's an array of objects..
42231             Roo.each(v, function(l) {
42232                 this.addItem(l);
42233             }, this);
42234              
42235         }
42236         
42237         
42238     },
42239     setFromData: function(v)
42240     {
42241         // this recieves an object, if setValues is called.
42242         this.reset();
42243         this.el.dom.value = v[this.displayField];
42244         this.hiddenEl.dom.value = v[this.valueField];
42245         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42246             return;
42247         }
42248         var kv = v[this.valueField];
42249         var dv = v[this.displayField];
42250         kv = typeof(kv) != 'string' ? '' : kv;
42251         dv = typeof(dv) != 'string' ? '' : dv;
42252         
42253         
42254         var keys = kv.split(',');
42255         var display = dv.split(',');
42256         for (var i = 0 ; i < keys.length; i++) {
42257             
42258             add = {};
42259             add[this.valueField] = keys[i];
42260             add[this.displayField] = display[i];
42261             this.addItem(add);
42262         }
42263       
42264         
42265     },
42266     
42267     /**
42268      * Validates the combox array value
42269      * @return {Boolean} True if the value is valid, else false
42270      */
42271     validate : function(){
42272         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42273             this.clearInvalid();
42274             return true;
42275         }
42276         return false;
42277     },
42278     
42279     validateValue : function(value){
42280         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42281         
42282     },
42283     
42284     /*@
42285      * overide
42286      * 
42287      */
42288     isDirty : function() {
42289         if(this.disabled) {
42290             return false;
42291         }
42292         
42293         try {
42294             var d = Roo.decode(String(this.originalValue));
42295         } catch (e) {
42296             return String(this.getValue()) !== String(this.originalValue);
42297         }
42298         
42299         var originalValue = [];
42300         
42301         for (var i = 0; i < d.length; i++){
42302             originalValue.push(d[i][this.valueField]);
42303         }
42304         
42305         return String(this.getValue()) !== String(originalValue.join(','));
42306         
42307     }
42308     
42309 });
42310
42311
42312
42313 /**
42314  * @class Roo.form.ComboBoxArray.Item
42315  * @extends Roo.BoxComponent
42316  * A selected item in the list
42317  *  Fred [x]  Brian [x]  [Pick another |v]
42318  * 
42319  * @constructor
42320  * Create a new item.
42321  * @param {Object} config Configuration options
42322  */
42323  
42324 Roo.form.ComboBoxArray.Item = function(config) {
42325     config.id = Roo.id();
42326     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42327 }
42328
42329 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42330     data : {},
42331     cb: false,
42332     displayField : false,
42333     tipField : false,
42334     
42335     
42336     defaultAutoCreate : {
42337         tag: 'div',
42338         cls: 'x-cbarray-item',
42339         cn : [ 
42340             { tag: 'div' },
42341             {
42342                 tag: 'img',
42343                 width:16,
42344                 height : 16,
42345                 src : Roo.BLANK_IMAGE_URL ,
42346                 align: 'center'
42347             }
42348         ]
42349         
42350     },
42351     
42352  
42353     onRender : function(ct, position)
42354     {
42355         Roo.form.Field.superclass.onRender.call(this, ct, position);
42356         
42357         if(!this.el){
42358             var cfg = this.getAutoCreate();
42359             this.el = ct.createChild(cfg, position);
42360         }
42361         
42362         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42363         
42364         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42365             this.cb.renderer(this.data) :
42366             String.format('{0}',this.data[this.displayField]);
42367         
42368             
42369         this.el.child('div').dom.setAttribute('qtip',
42370                         String.format('{0}',this.data[this.tipField])
42371         );
42372         
42373         this.el.child('img').on('click', this.remove, this);
42374         
42375     },
42376    
42377     remove : function()
42378     {
42379         if(this.cb.disabled){
42380             return;
42381         }
42382         
42383         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42384             this.cb.items.remove(this);
42385             this.el.child('img').un('click', this.remove, this);
42386             this.el.remove();
42387             this.cb.updateHiddenEl();
42388
42389             this.cb.fireEvent('remove', this.cb, this);
42390         }
42391         
42392     }
42393 });/*
42394  * Based on:
42395  * Ext JS Library 1.1.1
42396  * Copyright(c) 2006-2007, Ext JS, LLC.
42397  *
42398  * Originally Released Under LGPL - original licence link has changed is not relivant.
42399  *
42400  * Fork - LGPL
42401  * <script type="text/javascript">
42402  */
42403 /**
42404  * @class Roo.form.Checkbox
42405  * @extends Roo.form.Field
42406  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42407  * @constructor
42408  * Creates a new Checkbox
42409  * @param {Object} config Configuration options
42410  */
42411 Roo.form.Checkbox = function(config){
42412     Roo.form.Checkbox.superclass.constructor.call(this, config);
42413     this.addEvents({
42414         /**
42415          * @event check
42416          * Fires when the checkbox is checked or unchecked.
42417              * @param {Roo.form.Checkbox} this This checkbox
42418              * @param {Boolean} checked The new checked value
42419              */
42420         check : true
42421     });
42422 };
42423
42424 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
42425     /**
42426      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42427      */
42428     focusClass : undefined,
42429     /**
42430      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42431      */
42432     fieldClass: "x-form-field",
42433     /**
42434      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
42435      */
42436     checked: false,
42437     /**
42438      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42439      * {tag: "input", type: "checkbox", autocomplete: "off"})
42440      */
42441     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42442     /**
42443      * @cfg {String} boxLabel The text that appears beside the checkbox
42444      */
42445     boxLabel : "",
42446     /**
42447      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
42448      */  
42449     inputValue : '1',
42450     /**
42451      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
42452      */
42453      valueOff: '0', // value when not checked..
42454
42455     actionMode : 'viewEl', 
42456     //
42457     // private
42458     itemCls : 'x-menu-check-item x-form-item',
42459     groupClass : 'x-menu-group-item',
42460     inputType : 'hidden',
42461     
42462     
42463     inSetChecked: false, // check that we are not calling self...
42464     
42465     inputElement: false, // real input element?
42466     basedOn: false, // ????
42467     
42468     isFormField: true, // not sure where this is needed!!!!
42469
42470     onResize : function(){
42471         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42472         if(!this.boxLabel){
42473             this.el.alignTo(this.wrap, 'c-c');
42474         }
42475     },
42476
42477     initEvents : function(){
42478         Roo.form.Checkbox.superclass.initEvents.call(this);
42479         this.el.on("click", this.onClick,  this);
42480         this.el.on("change", this.onClick,  this);
42481     },
42482
42483
42484     getResizeEl : function(){
42485         return this.wrap;
42486     },
42487
42488     getPositionEl : function(){
42489         return this.wrap;
42490     },
42491
42492     // private
42493     onRender : function(ct, position){
42494         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42495         /*
42496         if(this.inputValue !== undefined){
42497             this.el.dom.value = this.inputValue;
42498         }
42499         */
42500         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42501         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42502         var viewEl = this.wrap.createChild({ 
42503             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42504         this.viewEl = viewEl;   
42505         this.wrap.on('click', this.onClick,  this); 
42506         
42507         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42508         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42509         
42510         
42511         
42512         if(this.boxLabel){
42513             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42514         //    viewEl.on('click', this.onClick,  this); 
42515         }
42516         //if(this.checked){
42517             this.setChecked(this.checked);
42518         //}else{
42519             //this.checked = this.el.dom;
42520         //}
42521
42522     },
42523
42524     // private
42525     initValue : Roo.emptyFn,
42526
42527     /**
42528      * Returns the checked state of the checkbox.
42529      * @return {Boolean} True if checked, else false
42530      */
42531     getValue : function(){
42532         if(this.el){
42533             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
42534         }
42535         return this.valueOff;
42536         
42537     },
42538
42539         // private
42540     onClick : function(){ 
42541         if (this.disabled) {
42542             return;
42543         }
42544         this.setChecked(!this.checked);
42545
42546         //if(this.el.dom.checked != this.checked){
42547         //    this.setValue(this.el.dom.checked);
42548        // }
42549     },
42550
42551     /**
42552      * Sets the checked state of the checkbox.
42553      * On is always based on a string comparison between inputValue and the param.
42554      * @param {Boolean/String} value - the value to set 
42555      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42556      */
42557     setValue : function(v,suppressEvent){
42558         
42559         
42560         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
42561         //if(this.el && this.el.dom){
42562         //    this.el.dom.checked = this.checked;
42563         //    this.el.dom.defaultChecked = this.checked;
42564         //}
42565         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
42566         //this.fireEvent("check", this, this.checked);
42567     },
42568     // private..
42569     setChecked : function(state,suppressEvent)
42570     {
42571         if (this.inSetChecked) {
42572             this.checked = state;
42573             return;
42574         }
42575         
42576     
42577         if(this.wrap){
42578             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
42579         }
42580         this.checked = state;
42581         if(suppressEvent !== true){
42582             this.fireEvent('check', this, state);
42583         }
42584         this.inSetChecked = true;
42585         this.el.dom.value = state ? this.inputValue : this.valueOff;
42586         this.inSetChecked = false;
42587         
42588     },
42589     // handle setting of hidden value by some other method!!?!?
42590     setFromHidden: function()
42591     {
42592         if(!this.el){
42593             return;
42594         }
42595         //console.log("SET FROM HIDDEN");
42596         //alert('setFrom hidden');
42597         this.setValue(this.el.dom.value);
42598     },
42599     
42600     onDestroy : function()
42601     {
42602         if(this.viewEl){
42603             Roo.get(this.viewEl).remove();
42604         }
42605          
42606         Roo.form.Checkbox.superclass.onDestroy.call(this);
42607     },
42608     
42609     setBoxLabel : function(str)
42610     {
42611         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
42612     }
42613
42614 });/*
42615  * Based on:
42616  * Ext JS Library 1.1.1
42617  * Copyright(c) 2006-2007, Ext JS, LLC.
42618  *
42619  * Originally Released Under LGPL - original licence link has changed is not relivant.
42620  *
42621  * Fork - LGPL
42622  * <script type="text/javascript">
42623  */
42624  
42625 /**
42626  * @class Roo.form.Radio
42627  * @extends Roo.form.Checkbox
42628  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
42629  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
42630  * @constructor
42631  * Creates a new Radio
42632  * @param {Object} config Configuration options
42633  */
42634 Roo.form.Radio = function(){
42635     Roo.form.Radio.superclass.constructor.apply(this, arguments);
42636 };
42637 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
42638     inputType: 'radio',
42639
42640     /**
42641      * If this radio is part of a group, it will return the selected value
42642      * @return {String}
42643      */
42644     getGroupValue : function(){
42645         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
42646     },
42647     
42648     
42649     onRender : function(ct, position){
42650         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42651         
42652         if(this.inputValue !== undefined){
42653             this.el.dom.value = this.inputValue;
42654         }
42655          
42656         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
42657         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
42658         //var viewEl = this.wrap.createChild({ 
42659         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
42660         //this.viewEl = viewEl;   
42661         //this.wrap.on('click', this.onClick,  this); 
42662         
42663         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42664         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
42665         
42666         
42667         
42668         if(this.boxLabel){
42669             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
42670         //    viewEl.on('click', this.onClick,  this); 
42671         }
42672          if(this.checked){
42673             this.el.dom.checked =   'checked' ;
42674         }
42675          
42676     } 
42677     
42678     
42679 });//<script type="text/javascript">
42680
42681 /*
42682  * Based  Ext JS Library 1.1.1
42683  * Copyright(c) 2006-2007, Ext JS, LLC.
42684  * LGPL
42685  *
42686  */
42687  
42688 /**
42689  * @class Roo.HtmlEditorCore
42690  * @extends Roo.Component
42691  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
42692  *
42693  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42694  */
42695
42696 Roo.HtmlEditorCore = function(config){
42697     
42698     
42699     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
42700     
42701     
42702     this.addEvents({
42703         /**
42704          * @event initialize
42705          * Fires when the editor is fully initialized (including the iframe)
42706          * @param {Roo.HtmlEditorCore} this
42707          */
42708         initialize: true,
42709         /**
42710          * @event activate
42711          * Fires when the editor is first receives the focus. Any insertion must wait
42712          * until after this event.
42713          * @param {Roo.HtmlEditorCore} this
42714          */
42715         activate: true,
42716          /**
42717          * @event beforesync
42718          * Fires before the textarea is updated with content from the editor iframe. Return false
42719          * to cancel the sync.
42720          * @param {Roo.HtmlEditorCore} this
42721          * @param {String} html
42722          */
42723         beforesync: true,
42724          /**
42725          * @event beforepush
42726          * Fires before the iframe editor is updated with content from the textarea. Return false
42727          * to cancel the push.
42728          * @param {Roo.HtmlEditorCore} this
42729          * @param {String} html
42730          */
42731         beforepush: true,
42732          /**
42733          * @event sync
42734          * Fires when the textarea is updated with content from the editor iframe.
42735          * @param {Roo.HtmlEditorCore} this
42736          * @param {String} html
42737          */
42738         sync: true,
42739          /**
42740          * @event push
42741          * Fires when the iframe editor is updated with content from the textarea.
42742          * @param {Roo.HtmlEditorCore} this
42743          * @param {String} html
42744          */
42745         push: true,
42746         
42747         /**
42748          * @event editorevent
42749          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42750          * @param {Roo.HtmlEditorCore} this
42751          */
42752         editorevent: true
42753         
42754     });
42755     
42756     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
42757     
42758     // defaults : white / black...
42759     this.applyBlacklists();
42760     
42761     
42762     
42763 };
42764
42765
42766 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
42767
42768
42769      /**
42770      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
42771      */
42772     
42773     owner : false,
42774     
42775      /**
42776      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42777      *                        Roo.resizable.
42778      */
42779     resizable : false,
42780      /**
42781      * @cfg {Number} height (in pixels)
42782      */   
42783     height: 300,
42784    /**
42785      * @cfg {Number} width (in pixels)
42786      */   
42787     width: 500,
42788     
42789     /**
42790      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42791      * 
42792      */
42793     stylesheets: false,
42794     
42795     // id of frame..
42796     frameId: false,
42797     
42798     // private properties
42799     validationEvent : false,
42800     deferHeight: true,
42801     initialized : false,
42802     activated : false,
42803     sourceEditMode : false,
42804     onFocus : Roo.emptyFn,
42805     iframePad:3,
42806     hideMode:'offsets',
42807     
42808     clearUp: true,
42809     
42810     // blacklist + whitelisted elements..
42811     black: false,
42812     white: false,
42813      
42814     
42815
42816     /**
42817      * Protected method that will not generally be called directly. It
42818      * is called when the editor initializes the iframe with HTML contents. Override this method if you
42819      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
42820      */
42821     getDocMarkup : function(){
42822         // body styles..
42823         var st = '';
42824         
42825         // inherit styels from page...?? 
42826         if (this.stylesheets === false) {
42827             
42828             Roo.get(document.head).select('style').each(function(node) {
42829                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42830             });
42831             
42832             Roo.get(document.head).select('link').each(function(node) { 
42833                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
42834             });
42835             
42836         } else if (!this.stylesheets.length) {
42837                 // simple..
42838                 st = '<style type="text/css">' +
42839                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42840                    '</style>';
42841         } else { 
42842             
42843         }
42844         
42845         st +=  '<style type="text/css">' +
42846             'IMG { cursor: pointer } ' +
42847         '</style>';
42848
42849         
42850         return '<html><head>' + st  +
42851             //<style type="text/css">' +
42852             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
42853             //'</style>' +
42854             ' </head><body class="roo-htmleditor-body"></body></html>';
42855     },
42856
42857     // private
42858     onRender : function(ct, position)
42859     {
42860         var _t = this;
42861         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
42862         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
42863         
42864         
42865         this.el.dom.style.border = '0 none';
42866         this.el.dom.setAttribute('tabIndex', -1);
42867         this.el.addClass('x-hidden hide');
42868         
42869         
42870         
42871         if(Roo.isIE){ // fix IE 1px bogus margin
42872             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
42873         }
42874        
42875         
42876         this.frameId = Roo.id();
42877         
42878          
42879         
42880         var iframe = this.owner.wrap.createChild({
42881             tag: 'iframe',
42882             cls: 'form-control', // bootstrap..
42883             id: this.frameId,
42884             name: this.frameId,
42885             frameBorder : 'no',
42886             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
42887         }, this.el
42888         );
42889         
42890         
42891         this.iframe = iframe.dom;
42892
42893          this.assignDocWin();
42894         
42895         this.doc.designMode = 'on';
42896        
42897         this.doc.open();
42898         this.doc.write(this.getDocMarkup());
42899         this.doc.close();
42900
42901         
42902         var task = { // must defer to wait for browser to be ready
42903             run : function(){
42904                 //console.log("run task?" + this.doc.readyState);
42905                 this.assignDocWin();
42906                 if(this.doc.body || this.doc.readyState == 'complete'){
42907                     try {
42908                         this.doc.designMode="on";
42909                     } catch (e) {
42910                         return;
42911                     }
42912                     Roo.TaskMgr.stop(task);
42913                     this.initEditor.defer(10, this);
42914                 }
42915             },
42916             interval : 10,
42917             duration: 10000,
42918             scope: this
42919         };
42920         Roo.TaskMgr.start(task);
42921
42922     },
42923
42924     // private
42925     onResize : function(w, h)
42926     {
42927          Roo.log('resize: ' +w + ',' + h );
42928         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
42929         if(!this.iframe){
42930             return;
42931         }
42932         if(typeof w == 'number'){
42933             
42934             this.iframe.style.width = w + 'px';
42935         }
42936         if(typeof h == 'number'){
42937             
42938             this.iframe.style.height = h + 'px';
42939             if(this.doc){
42940                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
42941             }
42942         }
42943         
42944     },
42945
42946     /**
42947      * Toggles the editor between standard and source edit mode.
42948      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42949      */
42950     toggleSourceEdit : function(sourceEditMode){
42951         
42952         this.sourceEditMode = sourceEditMode === true;
42953         
42954         if(this.sourceEditMode){
42955  
42956             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
42957             
42958         }else{
42959             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
42960             //this.iframe.className = '';
42961             this.deferFocus();
42962         }
42963         //this.setSize(this.owner.wrap.getSize());
42964         //this.fireEvent('editmodechange', this, this.sourceEditMode);
42965     },
42966
42967     
42968   
42969
42970     /**
42971      * Protected method that will not generally be called directly. If you need/want
42972      * custom HTML cleanup, this is the method you should override.
42973      * @param {String} html The HTML to be cleaned
42974      * return {String} The cleaned HTML
42975      */
42976     cleanHtml : function(html){
42977         html = String(html);
42978         if(html.length > 5){
42979             if(Roo.isSafari){ // strip safari nonsense
42980                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
42981             }
42982         }
42983         if(html == '&nbsp;'){
42984             html = '';
42985         }
42986         return html;
42987     },
42988
42989     /**
42990      * HTML Editor -> Textarea
42991      * Protected method that will not generally be called directly. Syncs the contents
42992      * of the editor iframe with the textarea.
42993      */
42994     syncValue : function(){
42995         if(this.initialized){
42996             var bd = (this.doc.body || this.doc.documentElement);
42997             //this.cleanUpPaste(); -- this is done else where and causes havoc..
42998             var html = bd.innerHTML;
42999             if(Roo.isSafari){
43000                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43001                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43002                 if(m && m[1]){
43003                     html = '<div style="'+m[0]+'">' + html + '</div>';
43004                 }
43005             }
43006             html = this.cleanHtml(html);
43007             // fix up the special chars.. normaly like back quotes in word...
43008             // however we do not want to do this with chinese..
43009             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
43010                 var cc = b.charCodeAt();
43011                 if (
43012                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43013                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43014                     (cc >= 0xf900 && cc < 0xfb00 )
43015                 ) {
43016                         return b;
43017                 }
43018                 return "&#"+cc+";" 
43019             });
43020             if(this.owner.fireEvent('beforesync', this, html) !== false){
43021                 this.el.dom.value = html;
43022                 this.owner.fireEvent('sync', this, html);
43023             }
43024         }
43025     },
43026
43027     /**
43028      * Protected method that will not generally be called directly. Pushes the value of the textarea
43029      * into the iframe editor.
43030      */
43031     pushValue : function(){
43032         if(this.initialized){
43033             var v = this.el.dom.value.trim();
43034             
43035 //            if(v.length < 1){
43036 //                v = '&#160;';
43037 //            }
43038             
43039             if(this.owner.fireEvent('beforepush', this, v) !== false){
43040                 var d = (this.doc.body || this.doc.documentElement);
43041                 d.innerHTML = v;
43042                 this.cleanUpPaste();
43043                 this.el.dom.value = d.innerHTML;
43044                 this.owner.fireEvent('push', this, v);
43045             }
43046         }
43047     },
43048
43049     // private
43050     deferFocus : function(){
43051         this.focus.defer(10, this);
43052     },
43053
43054     // doc'ed in Field
43055     focus : function(){
43056         if(this.win && !this.sourceEditMode){
43057             this.win.focus();
43058         }else{
43059             this.el.focus();
43060         }
43061     },
43062     
43063     assignDocWin: function()
43064     {
43065         var iframe = this.iframe;
43066         
43067          if(Roo.isIE){
43068             this.doc = iframe.contentWindow.document;
43069             this.win = iframe.contentWindow;
43070         } else {
43071 //            if (!Roo.get(this.frameId)) {
43072 //                return;
43073 //            }
43074 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43075 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43076             
43077             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43078                 return;
43079             }
43080             
43081             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43082             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43083         }
43084     },
43085     
43086     // private
43087     initEditor : function(){
43088         //console.log("INIT EDITOR");
43089         this.assignDocWin();
43090         
43091         
43092         
43093         this.doc.designMode="on";
43094         this.doc.open();
43095         this.doc.write(this.getDocMarkup());
43096         this.doc.close();
43097         
43098         var dbody = (this.doc.body || this.doc.documentElement);
43099         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43100         // this copies styles from the containing element into thsi one..
43101         // not sure why we need all of this..
43102         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43103         
43104         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43105         //ss['background-attachment'] = 'fixed'; // w3c
43106         dbody.bgProperties = 'fixed'; // ie
43107         //Roo.DomHelper.applyStyles(dbody, ss);
43108         Roo.EventManager.on(this.doc, {
43109             //'mousedown': this.onEditorEvent,
43110             'mouseup': this.onEditorEvent,
43111             'dblclick': this.onEditorEvent,
43112             'click': this.onEditorEvent,
43113             'keyup': this.onEditorEvent,
43114             buffer:100,
43115             scope: this
43116         });
43117         if(Roo.isGecko){
43118             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43119         }
43120         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43121             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43122         }
43123         this.initialized = true;
43124
43125         this.owner.fireEvent('initialize', this);
43126         this.pushValue();
43127     },
43128
43129     // private
43130     onDestroy : function(){
43131         
43132         
43133         
43134         if(this.rendered){
43135             
43136             //for (var i =0; i < this.toolbars.length;i++) {
43137             //    // fixme - ask toolbars for heights?
43138             //    this.toolbars[i].onDestroy();
43139            // }
43140             
43141             //this.wrap.dom.innerHTML = '';
43142             //this.wrap.remove();
43143         }
43144     },
43145
43146     // private
43147     onFirstFocus : function(){
43148         
43149         this.assignDocWin();
43150         
43151         
43152         this.activated = true;
43153          
43154     
43155         if(Roo.isGecko){ // prevent silly gecko errors
43156             this.win.focus();
43157             var s = this.win.getSelection();
43158             if(!s.focusNode || s.focusNode.nodeType != 3){
43159                 var r = s.getRangeAt(0);
43160                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43161                 r.collapse(true);
43162                 this.deferFocus();
43163             }
43164             try{
43165                 this.execCmd('useCSS', true);
43166                 this.execCmd('styleWithCSS', false);
43167             }catch(e){}
43168         }
43169         this.owner.fireEvent('activate', this);
43170     },
43171
43172     // private
43173     adjustFont: function(btn){
43174         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43175         //if(Roo.isSafari){ // safari
43176         //    adjust *= 2;
43177        // }
43178         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43179         if(Roo.isSafari){ // safari
43180             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43181             v =  (v < 10) ? 10 : v;
43182             v =  (v > 48) ? 48 : v;
43183             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43184             
43185         }
43186         
43187         
43188         v = Math.max(1, v+adjust);
43189         
43190         this.execCmd('FontSize', v  );
43191     },
43192
43193     onEditorEvent : function(e)
43194     {
43195         this.owner.fireEvent('editorevent', this, e);
43196       //  this.updateToolbar();
43197         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43198     },
43199
43200     insertTag : function(tg)
43201     {
43202         // could be a bit smarter... -> wrap the current selected tRoo..
43203         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
43204             
43205             range = this.createRange(this.getSelection());
43206             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43207             wrappingNode.appendChild(range.extractContents());
43208             range.insertNode(wrappingNode);
43209
43210             return;
43211             
43212             
43213             
43214         }
43215         this.execCmd("formatblock",   tg);
43216         
43217     },
43218     
43219     insertText : function(txt)
43220     {
43221         
43222         
43223         var range = this.createRange();
43224         range.deleteContents();
43225                //alert(Sender.getAttribute('label'));
43226                
43227         range.insertNode(this.doc.createTextNode(txt));
43228     } ,
43229     
43230      
43231
43232     /**
43233      * Executes a Midas editor command on the editor document and performs necessary focus and
43234      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43235      * @param {String} cmd The Midas command
43236      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43237      */
43238     relayCmd : function(cmd, value){
43239         this.win.focus();
43240         this.execCmd(cmd, value);
43241         this.owner.fireEvent('editorevent', this);
43242         //this.updateToolbar();
43243         this.owner.deferFocus();
43244     },
43245
43246     /**
43247      * Executes a Midas editor command directly on the editor document.
43248      * For visual commands, you should use {@link #relayCmd} instead.
43249      * <b>This should only be called after the editor is initialized.</b>
43250      * @param {String} cmd The Midas command
43251      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43252      */
43253     execCmd : function(cmd, value){
43254         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43255         this.syncValue();
43256     },
43257  
43258  
43259    
43260     /**
43261      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43262      * to insert tRoo.
43263      * @param {String} text | dom node.. 
43264      */
43265     insertAtCursor : function(text)
43266     {
43267         
43268         if(!this.activated){
43269             return;
43270         }
43271         /*
43272         if(Roo.isIE){
43273             this.win.focus();
43274             var r = this.doc.selection.createRange();
43275             if(r){
43276                 r.collapse(true);
43277                 r.pasteHTML(text);
43278                 this.syncValue();
43279                 this.deferFocus();
43280             
43281             }
43282             return;
43283         }
43284         */
43285         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43286             this.win.focus();
43287             
43288             
43289             // from jquery ui (MIT licenced)
43290             var range, node;
43291             var win = this.win;
43292             
43293             if (win.getSelection && win.getSelection().getRangeAt) {
43294                 range = win.getSelection().getRangeAt(0);
43295                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43296                 range.insertNode(node);
43297             } else if (win.document.selection && win.document.selection.createRange) {
43298                 // no firefox support
43299                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43300                 win.document.selection.createRange().pasteHTML(txt);
43301             } else {
43302                 // no firefox support
43303                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43304                 this.execCmd('InsertHTML', txt);
43305             } 
43306             
43307             this.syncValue();
43308             
43309             this.deferFocus();
43310         }
43311     },
43312  // private
43313     mozKeyPress : function(e){
43314         if(e.ctrlKey){
43315             var c = e.getCharCode(), cmd;
43316           
43317             if(c > 0){
43318                 c = String.fromCharCode(c).toLowerCase();
43319                 switch(c){
43320                     case 'b':
43321                         cmd = 'bold';
43322                         break;
43323                     case 'i':
43324                         cmd = 'italic';
43325                         break;
43326                     
43327                     case 'u':
43328                         cmd = 'underline';
43329                         break;
43330                     
43331                     case 'v':
43332                         this.cleanUpPaste.defer(100, this);
43333                         return;
43334                         
43335                 }
43336                 if(cmd){
43337                     this.win.focus();
43338                     this.execCmd(cmd);
43339                     this.deferFocus();
43340                     e.preventDefault();
43341                 }
43342                 
43343             }
43344         }
43345     },
43346
43347     // private
43348     fixKeys : function(){ // load time branching for fastest keydown performance
43349         if(Roo.isIE){
43350             return function(e){
43351                 var k = e.getKey(), r;
43352                 if(k == e.TAB){
43353                     e.stopEvent();
43354                     r = this.doc.selection.createRange();
43355                     if(r){
43356                         r.collapse(true);
43357                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43358                         this.deferFocus();
43359                     }
43360                     return;
43361                 }
43362                 
43363                 if(k == e.ENTER){
43364                     r = this.doc.selection.createRange();
43365                     if(r){
43366                         var target = r.parentElement();
43367                         if(!target || target.tagName.toLowerCase() != 'li'){
43368                             e.stopEvent();
43369                             r.pasteHTML('<br />');
43370                             r.collapse(false);
43371                             r.select();
43372                         }
43373                     }
43374                 }
43375                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43376                     this.cleanUpPaste.defer(100, this);
43377                     return;
43378                 }
43379                 
43380                 
43381             };
43382         }else if(Roo.isOpera){
43383             return function(e){
43384                 var k = e.getKey();
43385                 if(k == e.TAB){
43386                     e.stopEvent();
43387                     this.win.focus();
43388                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
43389                     this.deferFocus();
43390                 }
43391                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43392                     this.cleanUpPaste.defer(100, this);
43393                     return;
43394                 }
43395                 
43396             };
43397         }else if(Roo.isSafari){
43398             return function(e){
43399                 var k = e.getKey();
43400                 
43401                 if(k == e.TAB){
43402                     e.stopEvent();
43403                     this.execCmd('InsertText','\t');
43404                     this.deferFocus();
43405                     return;
43406                 }
43407                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43408                     this.cleanUpPaste.defer(100, this);
43409                     return;
43410                 }
43411                 
43412              };
43413         }
43414     }(),
43415     
43416     getAllAncestors: function()
43417     {
43418         var p = this.getSelectedNode();
43419         var a = [];
43420         if (!p) {
43421             a.push(p); // push blank onto stack..
43422             p = this.getParentElement();
43423         }
43424         
43425         
43426         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
43427             a.push(p);
43428             p = p.parentNode;
43429         }
43430         a.push(this.doc.body);
43431         return a;
43432     },
43433     lastSel : false,
43434     lastSelNode : false,
43435     
43436     
43437     getSelection : function() 
43438     {
43439         this.assignDocWin();
43440         return Roo.isIE ? this.doc.selection : this.win.getSelection();
43441     },
43442     
43443     getSelectedNode: function() 
43444     {
43445         // this may only work on Gecko!!!
43446         
43447         // should we cache this!!!!
43448         
43449         
43450         
43451          
43452         var range = this.createRange(this.getSelection()).cloneRange();
43453         
43454         if (Roo.isIE) {
43455             var parent = range.parentElement();
43456             while (true) {
43457                 var testRange = range.duplicate();
43458                 testRange.moveToElementText(parent);
43459                 if (testRange.inRange(range)) {
43460                     break;
43461                 }
43462                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
43463                     break;
43464                 }
43465                 parent = parent.parentElement;
43466             }
43467             return parent;
43468         }
43469         
43470         // is ancestor a text element.
43471         var ac =  range.commonAncestorContainer;
43472         if (ac.nodeType == 3) {
43473             ac = ac.parentNode;
43474         }
43475         
43476         var ar = ac.childNodes;
43477          
43478         var nodes = [];
43479         var other_nodes = [];
43480         var has_other_nodes = false;
43481         for (var i=0;i<ar.length;i++) {
43482             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
43483                 continue;
43484             }
43485             // fullly contained node.
43486             
43487             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
43488                 nodes.push(ar[i]);
43489                 continue;
43490             }
43491             
43492             // probably selected..
43493             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
43494                 other_nodes.push(ar[i]);
43495                 continue;
43496             }
43497             // outer..
43498             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
43499                 continue;
43500             }
43501             
43502             
43503             has_other_nodes = true;
43504         }
43505         if (!nodes.length && other_nodes.length) {
43506             nodes= other_nodes;
43507         }
43508         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
43509             return false;
43510         }
43511         
43512         return nodes[0];
43513     },
43514     createRange: function(sel)
43515     {
43516         // this has strange effects when using with 
43517         // top toolbar - not sure if it's a great idea.
43518         //this.editor.contentWindow.focus();
43519         if (typeof sel != "undefined") {
43520             try {
43521                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
43522             } catch(e) {
43523                 return this.doc.createRange();
43524             }
43525         } else {
43526             return this.doc.createRange();
43527         }
43528     },
43529     getParentElement: function()
43530     {
43531         
43532         this.assignDocWin();
43533         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
43534         
43535         var range = this.createRange(sel);
43536          
43537         try {
43538             var p = range.commonAncestorContainer;
43539             while (p.nodeType == 3) { // text node
43540                 p = p.parentNode;
43541             }
43542             return p;
43543         } catch (e) {
43544             return null;
43545         }
43546     
43547     },
43548     /***
43549      *
43550      * Range intersection.. the hard stuff...
43551      *  '-1' = before
43552      *  '0' = hits..
43553      *  '1' = after.
43554      *         [ -- selected range --- ]
43555      *   [fail]                        [fail]
43556      *
43557      *    basically..
43558      *      if end is before start or  hits it. fail.
43559      *      if start is after end or hits it fail.
43560      *
43561      *   if either hits (but other is outside. - then it's not 
43562      *   
43563      *    
43564      **/
43565     
43566     
43567     // @see http://www.thismuchiknow.co.uk/?p=64.
43568     rangeIntersectsNode : function(range, node)
43569     {
43570         var nodeRange = node.ownerDocument.createRange();
43571         try {
43572             nodeRange.selectNode(node);
43573         } catch (e) {
43574             nodeRange.selectNodeContents(node);
43575         }
43576     
43577         var rangeStartRange = range.cloneRange();
43578         rangeStartRange.collapse(true);
43579     
43580         var rangeEndRange = range.cloneRange();
43581         rangeEndRange.collapse(false);
43582     
43583         var nodeStartRange = nodeRange.cloneRange();
43584         nodeStartRange.collapse(true);
43585     
43586         var nodeEndRange = nodeRange.cloneRange();
43587         nodeEndRange.collapse(false);
43588     
43589         return rangeStartRange.compareBoundaryPoints(
43590                  Range.START_TO_START, nodeEndRange) == -1 &&
43591                rangeEndRange.compareBoundaryPoints(
43592                  Range.START_TO_START, nodeStartRange) == 1;
43593         
43594          
43595     },
43596     rangeCompareNode : function(range, node)
43597     {
43598         var nodeRange = node.ownerDocument.createRange();
43599         try {
43600             nodeRange.selectNode(node);
43601         } catch (e) {
43602             nodeRange.selectNodeContents(node);
43603         }
43604         
43605         
43606         range.collapse(true);
43607     
43608         nodeRange.collapse(true);
43609      
43610         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
43611         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
43612          
43613         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
43614         
43615         var nodeIsBefore   =  ss == 1;
43616         var nodeIsAfter    = ee == -1;
43617         
43618         if (nodeIsBefore && nodeIsAfter) {
43619             return 0; // outer
43620         }
43621         if (!nodeIsBefore && nodeIsAfter) {
43622             return 1; //right trailed.
43623         }
43624         
43625         if (nodeIsBefore && !nodeIsAfter) {
43626             return 2;  // left trailed.
43627         }
43628         // fully contined.
43629         return 3;
43630     },
43631
43632     // private? - in a new class?
43633     cleanUpPaste :  function()
43634     {
43635         // cleans up the whole document..
43636         Roo.log('cleanuppaste');
43637         
43638         this.cleanUpChildren(this.doc.body);
43639         var clean = this.cleanWordChars(this.doc.body.innerHTML);
43640         if (clean != this.doc.body.innerHTML) {
43641             this.doc.body.innerHTML = clean;
43642         }
43643         
43644     },
43645     
43646     cleanWordChars : function(input) {// change the chars to hex code
43647         var he = Roo.HtmlEditorCore;
43648         
43649         var output = input;
43650         Roo.each(he.swapCodes, function(sw) { 
43651             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
43652             
43653             output = output.replace(swapper, sw[1]);
43654         });
43655         
43656         return output;
43657     },
43658     
43659     
43660     cleanUpChildren : function (n)
43661     {
43662         if (!n.childNodes.length) {
43663             return;
43664         }
43665         for (var i = n.childNodes.length-1; i > -1 ; i--) {
43666            this.cleanUpChild(n.childNodes[i]);
43667         }
43668     },
43669     
43670     
43671         
43672     
43673     cleanUpChild : function (node)
43674     {
43675         var ed = this;
43676         //console.log(node);
43677         if (node.nodeName == "#text") {
43678             // clean up silly Windows -- stuff?
43679             return; 
43680         }
43681         if (node.nodeName == "#comment") {
43682             node.parentNode.removeChild(node);
43683             // clean up silly Windows -- stuff?
43684             return; 
43685         }
43686         var lcname = node.tagName.toLowerCase();
43687         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
43688         // whitelist of tags..
43689         
43690         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
43691             // remove node.
43692             node.parentNode.removeChild(node);
43693             return;
43694             
43695         }
43696         
43697         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
43698         
43699         // remove <a name=....> as rendering on yahoo mailer is borked with this.
43700         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
43701         
43702         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
43703         //    remove_keep_children = true;
43704         //}
43705         
43706         if (remove_keep_children) {
43707             this.cleanUpChildren(node);
43708             // inserts everything just before this node...
43709             while (node.childNodes.length) {
43710                 var cn = node.childNodes[0];
43711                 node.removeChild(cn);
43712                 node.parentNode.insertBefore(cn, node);
43713             }
43714             node.parentNode.removeChild(node);
43715             return;
43716         }
43717         
43718         if (!node.attributes || !node.attributes.length) {
43719             this.cleanUpChildren(node);
43720             return;
43721         }
43722         
43723         function cleanAttr(n,v)
43724         {
43725             
43726             if (v.match(/^\./) || v.match(/^\//)) {
43727                 return;
43728             }
43729             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
43730                 return;
43731             }
43732             if (v.match(/^#/)) {
43733                 return;
43734             }
43735 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
43736             node.removeAttribute(n);
43737             
43738         }
43739         
43740         var cwhite = this.cwhite;
43741         var cblack = this.cblack;
43742             
43743         function cleanStyle(n,v)
43744         {
43745             if (v.match(/expression/)) { //XSS?? should we even bother..
43746                 node.removeAttribute(n);
43747                 return;
43748             }
43749             
43750             var parts = v.split(/;/);
43751             var clean = [];
43752             
43753             Roo.each(parts, function(p) {
43754                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
43755                 if (!p.length) {
43756                     return true;
43757                 }
43758                 var l = p.split(':').shift().replace(/\s+/g,'');
43759                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
43760                 
43761                 if ( cwhite.length && cblack.indexOf(l) > -1) {
43762 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43763                     //node.removeAttribute(n);
43764                     return true;
43765                 }
43766                 //Roo.log()
43767                 // only allow 'c whitelisted system attributes'
43768                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
43769 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
43770                     //node.removeAttribute(n);
43771                     return true;
43772                 }
43773                 
43774                 
43775                  
43776                 
43777                 clean.push(p);
43778                 return true;
43779             });
43780             if (clean.length) { 
43781                 node.setAttribute(n, clean.join(';'));
43782             } else {
43783                 node.removeAttribute(n);
43784             }
43785             
43786         }
43787         
43788         
43789         for (var i = node.attributes.length-1; i > -1 ; i--) {
43790             var a = node.attributes[i];
43791             //console.log(a);
43792             
43793             if (a.name.toLowerCase().substr(0,2)=='on')  {
43794                 node.removeAttribute(a.name);
43795                 continue;
43796             }
43797             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
43798                 node.removeAttribute(a.name);
43799                 continue;
43800             }
43801             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
43802                 cleanAttr(a.name,a.value); // fixme..
43803                 continue;
43804             }
43805             if (a.name == 'style') {
43806                 cleanStyle(a.name,a.value);
43807                 continue;
43808             }
43809             /// clean up MS crap..
43810             // tecnically this should be a list of valid class'es..
43811             
43812             
43813             if (a.name == 'class') {
43814                 if (a.value.match(/^Mso/)) {
43815                     node.className = '';
43816                 }
43817                 
43818                 if (a.value.match(/^body$/)) {
43819                     node.className = '';
43820                 }
43821                 continue;
43822             }
43823             
43824             // style cleanup!?
43825             // class cleanup?
43826             
43827         }
43828         
43829         
43830         this.cleanUpChildren(node);
43831         
43832         
43833     },
43834     
43835     /**
43836      * Clean up MS wordisms...
43837      */
43838     cleanWord : function(node)
43839     {
43840         
43841         
43842         if (!node) {
43843             this.cleanWord(this.doc.body);
43844             return;
43845         }
43846         if (node.nodeName == "#text") {
43847             // clean up silly Windows -- stuff?
43848             return; 
43849         }
43850         if (node.nodeName == "#comment") {
43851             node.parentNode.removeChild(node);
43852             // clean up silly Windows -- stuff?
43853             return; 
43854         }
43855         
43856         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
43857             node.parentNode.removeChild(node);
43858             return;
43859         }
43860         
43861         // remove - but keep children..
43862         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
43863             while (node.childNodes.length) {
43864                 var cn = node.childNodes[0];
43865                 node.removeChild(cn);
43866                 node.parentNode.insertBefore(cn, node);
43867             }
43868             node.parentNode.removeChild(node);
43869             this.iterateChildren(node, this.cleanWord);
43870             return;
43871         }
43872         // clean styles
43873         if (node.className.length) {
43874             
43875             var cn = node.className.split(/\W+/);
43876             var cna = [];
43877             Roo.each(cn, function(cls) {
43878                 if (cls.match(/Mso[a-zA-Z]+/)) {
43879                     return;
43880                 }
43881                 cna.push(cls);
43882             });
43883             node.className = cna.length ? cna.join(' ') : '';
43884             if (!cna.length) {
43885                 node.removeAttribute("class");
43886             }
43887         }
43888         
43889         if (node.hasAttribute("lang")) {
43890             node.removeAttribute("lang");
43891         }
43892         
43893         if (node.hasAttribute("style")) {
43894             
43895             var styles = node.getAttribute("style").split(";");
43896             var nstyle = [];
43897             Roo.each(styles, function(s) {
43898                 if (!s.match(/:/)) {
43899                     return;
43900                 }
43901                 var kv = s.split(":");
43902                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
43903                     return;
43904                 }
43905                 // what ever is left... we allow.
43906                 nstyle.push(s);
43907             });
43908             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43909             if (!nstyle.length) {
43910                 node.removeAttribute('style');
43911             }
43912         }
43913         this.iterateChildren(node, this.cleanWord);
43914         
43915         
43916         
43917     },
43918     /**
43919      * iterateChildren of a Node, calling fn each time, using this as the scole..
43920      * @param {DomNode} node node to iterate children of.
43921      * @param {Function} fn method of this class to call on each item.
43922      */
43923     iterateChildren : function(node, fn)
43924     {
43925         if (!node.childNodes.length) {
43926                 return;
43927         }
43928         for (var i = node.childNodes.length-1; i > -1 ; i--) {
43929            fn.call(this, node.childNodes[i])
43930         }
43931     },
43932     
43933     
43934     /**
43935      * cleanTableWidths.
43936      *
43937      * Quite often pasting from word etc.. results in tables with column and widths.
43938      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
43939      *
43940      */
43941     cleanTableWidths : function(node)
43942     {
43943          
43944          
43945         if (!node) {
43946             this.cleanTableWidths(this.doc.body);
43947             return;
43948         }
43949         
43950         // ignore list...
43951         if (node.nodeName == "#text" || node.nodeName == "#comment") {
43952             return; 
43953         }
43954         Roo.log(node.tagName);
43955         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
43956             this.iterateChildren(node, this.cleanTableWidths);
43957             return;
43958         }
43959         if (node.hasAttribute('width')) {
43960             node.removeAttribute('width');
43961         }
43962         
43963          
43964         if (node.hasAttribute("style")) {
43965             // pretty basic...
43966             
43967             var styles = node.getAttribute("style").split(";");
43968             var nstyle = [];
43969             Roo.each(styles, function(s) {
43970                 if (!s.match(/:/)) {
43971                     return;
43972                 }
43973                 var kv = s.split(":");
43974                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
43975                     return;
43976                 }
43977                 // what ever is left... we allow.
43978                 nstyle.push(s);
43979             });
43980             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
43981             if (!nstyle.length) {
43982                 node.removeAttribute('style');
43983             }
43984         }
43985         
43986         this.iterateChildren(node, this.cleanTableWidths);
43987         
43988         
43989     },
43990     
43991     
43992     
43993     
43994     domToHTML : function(currentElement, depth, nopadtext) {
43995         
43996         depth = depth || 0;
43997         nopadtext = nopadtext || false;
43998     
43999         if (!currentElement) {
44000             return this.domToHTML(this.doc.body);
44001         }
44002         
44003         //Roo.log(currentElement);
44004         var j;
44005         var allText = false;
44006         var nodeName = currentElement.nodeName;
44007         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44008         
44009         if  (nodeName == '#text') {
44010             
44011             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44012         }
44013         
44014         
44015         var ret = '';
44016         if (nodeName != 'BODY') {
44017              
44018             var i = 0;
44019             // Prints the node tagName, such as <A>, <IMG>, etc
44020             if (tagName) {
44021                 var attr = [];
44022                 for(i = 0; i < currentElement.attributes.length;i++) {
44023                     // quoting?
44024                     var aname = currentElement.attributes.item(i).name;
44025                     if (!currentElement.attributes.item(i).value.length) {
44026                         continue;
44027                     }
44028                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44029                 }
44030                 
44031                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44032             } 
44033             else {
44034                 
44035                 // eack
44036             }
44037         } else {
44038             tagName = false;
44039         }
44040         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44041             return ret;
44042         }
44043         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44044             nopadtext = true;
44045         }
44046         
44047         
44048         // Traverse the tree
44049         i = 0;
44050         var currentElementChild = currentElement.childNodes.item(i);
44051         var allText = true;
44052         var innerHTML  = '';
44053         lastnode = '';
44054         while (currentElementChild) {
44055             // Formatting code (indent the tree so it looks nice on the screen)
44056             var nopad = nopadtext;
44057             if (lastnode == 'SPAN') {
44058                 nopad  = true;
44059             }
44060             // text
44061             if  (currentElementChild.nodeName == '#text') {
44062                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44063                 toadd = nopadtext ? toadd : toadd.trim();
44064                 if (!nopad && toadd.length > 80) {
44065                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44066                 }
44067                 innerHTML  += toadd;
44068                 
44069                 i++;
44070                 currentElementChild = currentElement.childNodes.item(i);
44071                 lastNode = '';
44072                 continue;
44073             }
44074             allText = false;
44075             
44076             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44077                 
44078             // Recursively traverse the tree structure of the child node
44079             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44080             lastnode = currentElementChild.nodeName;
44081             i++;
44082             currentElementChild=currentElement.childNodes.item(i);
44083         }
44084         
44085         ret += innerHTML;
44086         
44087         if (!allText) {
44088                 // The remaining code is mostly for formatting the tree
44089             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44090         }
44091         
44092         
44093         if (tagName) {
44094             ret+= "</"+tagName+">";
44095         }
44096         return ret;
44097         
44098     },
44099         
44100     applyBlacklists : function()
44101     {
44102         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44103         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44104         
44105         this.white = [];
44106         this.black = [];
44107         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44108             if (b.indexOf(tag) > -1) {
44109                 return;
44110             }
44111             this.white.push(tag);
44112             
44113         }, this);
44114         
44115         Roo.each(w, function(tag) {
44116             if (b.indexOf(tag) > -1) {
44117                 return;
44118             }
44119             if (this.white.indexOf(tag) > -1) {
44120                 return;
44121             }
44122             this.white.push(tag);
44123             
44124         }, this);
44125         
44126         
44127         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44128             if (w.indexOf(tag) > -1) {
44129                 return;
44130             }
44131             this.black.push(tag);
44132             
44133         }, this);
44134         
44135         Roo.each(b, function(tag) {
44136             if (w.indexOf(tag) > -1) {
44137                 return;
44138             }
44139             if (this.black.indexOf(tag) > -1) {
44140                 return;
44141             }
44142             this.black.push(tag);
44143             
44144         }, this);
44145         
44146         
44147         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44148         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44149         
44150         this.cwhite = [];
44151         this.cblack = [];
44152         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44153             if (b.indexOf(tag) > -1) {
44154                 return;
44155             }
44156             this.cwhite.push(tag);
44157             
44158         }, this);
44159         
44160         Roo.each(w, function(tag) {
44161             if (b.indexOf(tag) > -1) {
44162                 return;
44163             }
44164             if (this.cwhite.indexOf(tag) > -1) {
44165                 return;
44166             }
44167             this.cwhite.push(tag);
44168             
44169         }, this);
44170         
44171         
44172         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44173             if (w.indexOf(tag) > -1) {
44174                 return;
44175             }
44176             this.cblack.push(tag);
44177             
44178         }, this);
44179         
44180         Roo.each(b, function(tag) {
44181             if (w.indexOf(tag) > -1) {
44182                 return;
44183             }
44184             if (this.cblack.indexOf(tag) > -1) {
44185                 return;
44186             }
44187             this.cblack.push(tag);
44188             
44189         }, this);
44190     },
44191     
44192     setStylesheets : function(stylesheets)
44193     {
44194         if(typeof(stylesheets) == 'string'){
44195             Roo.get(this.iframe.contentDocument.head).createChild({
44196                 tag : 'link',
44197                 rel : 'stylesheet',
44198                 type : 'text/css',
44199                 href : stylesheets
44200             });
44201             
44202             return;
44203         }
44204         var _this = this;
44205      
44206         Roo.each(stylesheets, function(s) {
44207             if(!s.length){
44208                 return;
44209             }
44210             
44211             Roo.get(_this.iframe.contentDocument.head).createChild({
44212                 tag : 'link',
44213                 rel : 'stylesheet',
44214                 type : 'text/css',
44215                 href : s
44216             });
44217         });
44218
44219         
44220     },
44221     
44222     removeStylesheets : function()
44223     {
44224         var _this = this;
44225         
44226         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44227             s.remove();
44228         });
44229     }
44230     
44231     // hide stuff that is not compatible
44232     /**
44233      * @event blur
44234      * @hide
44235      */
44236     /**
44237      * @event change
44238      * @hide
44239      */
44240     /**
44241      * @event focus
44242      * @hide
44243      */
44244     /**
44245      * @event specialkey
44246      * @hide
44247      */
44248     /**
44249      * @cfg {String} fieldClass @hide
44250      */
44251     /**
44252      * @cfg {String} focusClass @hide
44253      */
44254     /**
44255      * @cfg {String} autoCreate @hide
44256      */
44257     /**
44258      * @cfg {String} inputType @hide
44259      */
44260     /**
44261      * @cfg {String} invalidClass @hide
44262      */
44263     /**
44264      * @cfg {String} invalidText @hide
44265      */
44266     /**
44267      * @cfg {String} msgFx @hide
44268      */
44269     /**
44270      * @cfg {String} validateOnBlur @hide
44271      */
44272 });
44273
44274 Roo.HtmlEditorCore.white = [
44275         'area', 'br', 'img', 'input', 'hr', 'wbr',
44276         
44277        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44278        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44279        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44280        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44281        'table',   'ul',         'xmp', 
44282        
44283        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44284       'thead',   'tr', 
44285      
44286       'dir', 'menu', 'ol', 'ul', 'dl',
44287        
44288       'embed',  'object'
44289 ];
44290
44291
44292 Roo.HtmlEditorCore.black = [
44293     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44294         'applet', // 
44295         'base',   'basefont', 'bgsound', 'blink',  'body', 
44296         'frame',  'frameset', 'head',    'html',   'ilayer', 
44297         'iframe', 'layer',  'link',     'meta',    'object',   
44298         'script', 'style' ,'title',  'xml' // clean later..
44299 ];
44300 Roo.HtmlEditorCore.clean = [
44301     'script', 'style', 'title', 'xml'
44302 ];
44303 Roo.HtmlEditorCore.remove = [
44304     'font'
44305 ];
44306 // attributes..
44307
44308 Roo.HtmlEditorCore.ablack = [
44309     'on'
44310 ];
44311     
44312 Roo.HtmlEditorCore.aclean = [ 
44313     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44314 ];
44315
44316 // protocols..
44317 Roo.HtmlEditorCore.pwhite= [
44318         'http',  'https',  'mailto'
44319 ];
44320
44321 // white listed style attributes.
44322 Roo.HtmlEditorCore.cwhite= [
44323       //  'text-align', /// default is to allow most things..
44324       
44325          
44326 //        'font-size'//??
44327 ];
44328
44329 // black listed style attributes.
44330 Roo.HtmlEditorCore.cblack= [
44331       //  'font-size' -- this can be set by the project 
44332 ];
44333
44334
44335 Roo.HtmlEditorCore.swapCodes   =[ 
44336     [    8211, "--" ], 
44337     [    8212, "--" ], 
44338     [    8216,  "'" ],  
44339     [    8217, "'" ],  
44340     [    8220, '"' ],  
44341     [    8221, '"' ],  
44342     [    8226, "*" ],  
44343     [    8230, "..." ]
44344 ]; 
44345
44346     //<script type="text/javascript">
44347
44348 /*
44349  * Ext JS Library 1.1.1
44350  * Copyright(c) 2006-2007, Ext JS, LLC.
44351  * Licence LGPL
44352  * 
44353  */
44354  
44355  
44356 Roo.form.HtmlEditor = function(config){
44357     
44358     
44359     
44360     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
44361     
44362     if (!this.toolbars) {
44363         this.toolbars = [];
44364     }
44365     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
44366     
44367     
44368 };
44369
44370 /**
44371  * @class Roo.form.HtmlEditor
44372  * @extends Roo.form.Field
44373  * Provides a lightweight HTML Editor component.
44374  *
44375  * This has been tested on Fireforx / Chrome.. IE may not be so great..
44376  * 
44377  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
44378  * supported by this editor.</b><br/><br/>
44379  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
44380  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44381  */
44382 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
44383     /**
44384      * @cfg {Boolean} clearUp
44385      */
44386     clearUp : true,
44387       /**
44388      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
44389      */
44390     toolbars : false,
44391    
44392      /**
44393      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44394      *                        Roo.resizable.
44395      */
44396     resizable : false,
44397      /**
44398      * @cfg {Number} height (in pixels)
44399      */   
44400     height: 300,
44401    /**
44402      * @cfg {Number} width (in pixels)
44403      */   
44404     width: 500,
44405     
44406     /**
44407      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44408      * 
44409      */
44410     stylesheets: false,
44411     
44412     
44413      /**
44414      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
44415      * 
44416      */
44417     cblack: false,
44418     /**
44419      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
44420      * 
44421      */
44422     cwhite: false,
44423     
44424      /**
44425      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
44426      * 
44427      */
44428     black: false,
44429     /**
44430      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
44431      * 
44432      */
44433     white: false,
44434     
44435     // id of frame..
44436     frameId: false,
44437     
44438     // private properties
44439     validationEvent : false,
44440     deferHeight: true,
44441     initialized : false,
44442     activated : false,
44443     
44444     onFocus : Roo.emptyFn,
44445     iframePad:3,
44446     hideMode:'offsets',
44447     
44448     actionMode : 'container', // defaults to hiding it...
44449     
44450     defaultAutoCreate : { // modified by initCompnoent..
44451         tag: "textarea",
44452         style:"width:500px;height:300px;",
44453         autocomplete: "new-password"
44454     },
44455
44456     // private
44457     initComponent : function(){
44458         this.addEvents({
44459             /**
44460              * @event initialize
44461              * Fires when the editor is fully initialized (including the iframe)
44462              * @param {HtmlEditor} this
44463              */
44464             initialize: true,
44465             /**
44466              * @event activate
44467              * Fires when the editor is first receives the focus. Any insertion must wait
44468              * until after this event.
44469              * @param {HtmlEditor} this
44470              */
44471             activate: true,
44472              /**
44473              * @event beforesync
44474              * Fires before the textarea is updated with content from the editor iframe. Return false
44475              * to cancel the sync.
44476              * @param {HtmlEditor} this
44477              * @param {String} html
44478              */
44479             beforesync: true,
44480              /**
44481              * @event beforepush
44482              * Fires before the iframe editor is updated with content from the textarea. Return false
44483              * to cancel the push.
44484              * @param {HtmlEditor} this
44485              * @param {String} html
44486              */
44487             beforepush: true,
44488              /**
44489              * @event sync
44490              * Fires when the textarea is updated with content from the editor iframe.
44491              * @param {HtmlEditor} this
44492              * @param {String} html
44493              */
44494             sync: true,
44495              /**
44496              * @event push
44497              * Fires when the iframe editor is updated with content from the textarea.
44498              * @param {HtmlEditor} this
44499              * @param {String} html
44500              */
44501             push: true,
44502              /**
44503              * @event editmodechange
44504              * Fires when the editor switches edit modes
44505              * @param {HtmlEditor} this
44506              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
44507              */
44508             editmodechange: true,
44509             /**
44510              * @event editorevent
44511              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44512              * @param {HtmlEditor} this
44513              */
44514             editorevent: true,
44515             /**
44516              * @event firstfocus
44517              * Fires when on first focus - needed by toolbars..
44518              * @param {HtmlEditor} this
44519              */
44520             firstfocus: true,
44521             /**
44522              * @event autosave
44523              * Auto save the htmlEditor value as a file into Events
44524              * @param {HtmlEditor} this
44525              */
44526             autosave: true,
44527             /**
44528              * @event savedpreview
44529              * preview the saved version of htmlEditor
44530              * @param {HtmlEditor} this
44531              */
44532             savedpreview: true,
44533             
44534             /**
44535             * @event stylesheetsclick
44536             * Fires when press the Sytlesheets button
44537             * @param {Roo.HtmlEditorCore} this
44538             */
44539             stylesheetsclick: true
44540         });
44541         this.defaultAutoCreate =  {
44542             tag: "textarea",
44543             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
44544             autocomplete: "new-password"
44545         };
44546     },
44547
44548     /**
44549      * Protected method that will not generally be called directly. It
44550      * is called when the editor creates its toolbar. Override this method if you need to
44551      * add custom toolbar buttons.
44552      * @param {HtmlEditor} editor
44553      */
44554     createToolbar : function(editor){
44555         Roo.log("create toolbars");
44556         if (!editor.toolbars || !editor.toolbars.length) {
44557             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
44558         }
44559         
44560         for (var i =0 ; i < editor.toolbars.length;i++) {
44561             editor.toolbars[i] = Roo.factory(
44562                     typeof(editor.toolbars[i]) == 'string' ?
44563                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
44564                 Roo.form.HtmlEditor);
44565             editor.toolbars[i].init(editor);
44566         }
44567          
44568         
44569     },
44570
44571      
44572     // private
44573     onRender : function(ct, position)
44574     {
44575         var _t = this;
44576         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
44577         
44578         this.wrap = this.el.wrap({
44579             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
44580         });
44581         
44582         this.editorcore.onRender(ct, position);
44583          
44584         if (this.resizable) {
44585             this.resizeEl = new Roo.Resizable(this.wrap, {
44586                 pinned : true,
44587                 wrap: true,
44588                 dynamic : true,
44589                 minHeight : this.height,
44590                 height: this.height,
44591                 handles : this.resizable,
44592                 width: this.width,
44593                 listeners : {
44594                     resize : function(r, w, h) {
44595                         _t.onResize(w,h); // -something
44596                     }
44597                 }
44598             });
44599             
44600         }
44601         this.createToolbar(this);
44602        
44603         
44604         if(!this.width){
44605             this.setSize(this.wrap.getSize());
44606         }
44607         if (this.resizeEl) {
44608             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
44609             // should trigger onReize..
44610         }
44611         
44612         this.keyNav = new Roo.KeyNav(this.el, {
44613             
44614             "tab" : function(e){
44615                 e.preventDefault();
44616                 
44617                 var value = this.getValue();
44618                 
44619                 var start = this.el.dom.selectionStart;
44620                 var end = this.el.dom.selectionEnd;
44621                 
44622                 if(!e.shiftKey){
44623                     
44624                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
44625                     this.el.dom.setSelectionRange(end + 1, end + 1);
44626                     return;
44627                 }
44628                 
44629                 var f = value.substring(0, start).split("\t");
44630                 
44631                 if(f.pop().length != 0){
44632                     return;
44633                 }
44634                 
44635                 this.setValue(f.join("\t") + value.substring(end));
44636                 this.el.dom.setSelectionRange(start - 1, start - 1);
44637                 
44638             },
44639             
44640             "home" : function(e){
44641                 e.preventDefault();
44642                 
44643                 var curr = this.el.dom.selectionStart;
44644                 var lines = this.getValue().split("\n");
44645                 
44646                 if(!lines.length){
44647                     return;
44648                 }
44649                 
44650                 if(e.ctrlKey){
44651                     this.el.dom.setSelectionRange(0, 0);
44652                     return;
44653                 }
44654                 
44655                 var pos = 0;
44656                 
44657                 for (var i = 0; i < lines.length;i++) {
44658                     pos += lines[i].length;
44659                     
44660                     if(i != 0){
44661                         pos += 1;
44662                     }
44663                     
44664                     if(pos < curr){
44665                         continue;
44666                     }
44667                     
44668                     pos -= lines[i].length;
44669                     
44670                     break;
44671                 }
44672                 
44673                 if(!e.shiftKey){
44674                     this.el.dom.setSelectionRange(pos, pos);
44675                     return;
44676                 }
44677                 
44678                 this.el.dom.selectionStart = pos;
44679                 this.el.dom.selectionEnd = curr;
44680             },
44681             
44682             "end" : function(e){
44683                 e.preventDefault();
44684                 
44685                 var curr = this.el.dom.selectionStart;
44686                 var lines = this.getValue().split("\n");
44687                 
44688                 if(!lines.length){
44689                     return;
44690                 }
44691                 
44692                 if(e.ctrlKey){
44693                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
44694                     return;
44695                 }
44696                 
44697                 var pos = 0;
44698                 
44699                 for (var i = 0; i < lines.length;i++) {
44700                     
44701                     pos += lines[i].length;
44702                     
44703                     if(i != 0){
44704                         pos += 1;
44705                     }
44706                     
44707                     if(pos < curr){
44708                         continue;
44709                     }
44710                     
44711                     break;
44712                 }
44713                 
44714                 if(!e.shiftKey){
44715                     this.el.dom.setSelectionRange(pos, pos);
44716                     return;
44717                 }
44718                 
44719                 this.el.dom.selectionStart = curr;
44720                 this.el.dom.selectionEnd = pos;
44721             },
44722
44723             scope : this,
44724
44725             doRelay : function(foo, bar, hname){
44726                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44727             },
44728
44729             forceKeyDown: true
44730         });
44731         
44732 //        if(this.autosave && this.w){
44733 //            this.autoSaveFn = setInterval(this.autosave, 1000);
44734 //        }
44735     },
44736
44737     // private
44738     onResize : function(w, h)
44739     {
44740         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
44741         var ew = false;
44742         var eh = false;
44743         
44744         if(this.el ){
44745             if(typeof w == 'number'){
44746                 var aw = w - this.wrap.getFrameWidth('lr');
44747                 this.el.setWidth(this.adjustWidth('textarea', aw));
44748                 ew = aw;
44749             }
44750             if(typeof h == 'number'){
44751                 var tbh = 0;
44752                 for (var i =0; i < this.toolbars.length;i++) {
44753                     // fixme - ask toolbars for heights?
44754                     tbh += this.toolbars[i].tb.el.getHeight();
44755                     if (this.toolbars[i].footer) {
44756                         tbh += this.toolbars[i].footer.el.getHeight();
44757                     }
44758                 }
44759                 
44760                 
44761                 
44762                 
44763                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
44764                 ah -= 5; // knock a few pixes off for look..
44765 //                Roo.log(ah);
44766                 this.el.setHeight(this.adjustWidth('textarea', ah));
44767                 var eh = ah;
44768             }
44769         }
44770         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
44771         this.editorcore.onResize(ew,eh);
44772         
44773     },
44774
44775     /**
44776      * Toggles the editor between standard and source edit mode.
44777      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44778      */
44779     toggleSourceEdit : function(sourceEditMode)
44780     {
44781         this.editorcore.toggleSourceEdit(sourceEditMode);
44782         
44783         if(this.editorcore.sourceEditMode){
44784             Roo.log('editor - showing textarea');
44785             
44786 //            Roo.log('in');
44787 //            Roo.log(this.syncValue());
44788             this.editorcore.syncValue();
44789             this.el.removeClass('x-hidden');
44790             this.el.dom.removeAttribute('tabIndex');
44791             this.el.focus();
44792             
44793             for (var i = 0; i < this.toolbars.length; i++) {
44794                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44795                     this.toolbars[i].tb.hide();
44796                     this.toolbars[i].footer.hide();
44797                 }
44798             }
44799             
44800         }else{
44801             Roo.log('editor - hiding textarea');
44802 //            Roo.log('out')
44803 //            Roo.log(this.pushValue()); 
44804             this.editorcore.pushValue();
44805             
44806             this.el.addClass('x-hidden');
44807             this.el.dom.setAttribute('tabIndex', -1);
44808             
44809             for (var i = 0; i < this.toolbars.length; i++) {
44810                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
44811                     this.toolbars[i].tb.show();
44812                     this.toolbars[i].footer.show();
44813                 }
44814             }
44815             
44816             //this.deferFocus();
44817         }
44818         
44819         this.setSize(this.wrap.getSize());
44820         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
44821         
44822         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
44823     },
44824  
44825     // private (for BoxComponent)
44826     adjustSize : Roo.BoxComponent.prototype.adjustSize,
44827
44828     // private (for BoxComponent)
44829     getResizeEl : function(){
44830         return this.wrap;
44831     },
44832
44833     // private (for BoxComponent)
44834     getPositionEl : function(){
44835         return this.wrap;
44836     },
44837
44838     // private
44839     initEvents : function(){
44840         this.originalValue = this.getValue();
44841     },
44842
44843     /**
44844      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44845      * @method
44846      */
44847     markInvalid : Roo.emptyFn,
44848     /**
44849      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
44850      * @method
44851      */
44852     clearInvalid : Roo.emptyFn,
44853
44854     setValue : function(v){
44855         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
44856         this.editorcore.pushValue();
44857     },
44858
44859      
44860     // private
44861     deferFocus : function(){
44862         this.focus.defer(10, this);
44863     },
44864
44865     // doc'ed in Field
44866     focus : function(){
44867         this.editorcore.focus();
44868         
44869     },
44870       
44871
44872     // private
44873     onDestroy : function(){
44874         
44875         
44876         
44877         if(this.rendered){
44878             
44879             for (var i =0; i < this.toolbars.length;i++) {
44880                 // fixme - ask toolbars for heights?
44881                 this.toolbars[i].onDestroy();
44882             }
44883             
44884             this.wrap.dom.innerHTML = '';
44885             this.wrap.remove();
44886         }
44887     },
44888
44889     // private
44890     onFirstFocus : function(){
44891         //Roo.log("onFirstFocus");
44892         this.editorcore.onFirstFocus();
44893          for (var i =0; i < this.toolbars.length;i++) {
44894             this.toolbars[i].onFirstFocus();
44895         }
44896         
44897     },
44898     
44899     // private
44900     syncValue : function()
44901     {
44902         this.editorcore.syncValue();
44903     },
44904     
44905     pushValue : function()
44906     {
44907         this.editorcore.pushValue();
44908     },
44909     
44910     setStylesheets : function(stylesheets)
44911     {
44912         this.editorcore.setStylesheets(stylesheets);
44913     },
44914     
44915     removeStylesheets : function()
44916     {
44917         this.editorcore.removeStylesheets();
44918     }
44919      
44920     
44921     // hide stuff that is not compatible
44922     /**
44923      * @event blur
44924      * @hide
44925      */
44926     /**
44927      * @event change
44928      * @hide
44929      */
44930     /**
44931      * @event focus
44932      * @hide
44933      */
44934     /**
44935      * @event specialkey
44936      * @hide
44937      */
44938     /**
44939      * @cfg {String} fieldClass @hide
44940      */
44941     /**
44942      * @cfg {String} focusClass @hide
44943      */
44944     /**
44945      * @cfg {String} autoCreate @hide
44946      */
44947     /**
44948      * @cfg {String} inputType @hide
44949      */
44950     /**
44951      * @cfg {String} invalidClass @hide
44952      */
44953     /**
44954      * @cfg {String} invalidText @hide
44955      */
44956     /**
44957      * @cfg {String} msgFx @hide
44958      */
44959     /**
44960      * @cfg {String} validateOnBlur @hide
44961      */
44962 });
44963  
44964     // <script type="text/javascript">
44965 /*
44966  * Based on
44967  * Ext JS Library 1.1.1
44968  * Copyright(c) 2006-2007, Ext JS, LLC.
44969  *  
44970  
44971  */
44972
44973 /**
44974  * @class Roo.form.HtmlEditorToolbar1
44975  * Basic Toolbar
44976  * 
44977  * Usage:
44978  *
44979  new Roo.form.HtmlEditor({
44980     ....
44981     toolbars : [
44982         new Roo.form.HtmlEditorToolbar1({
44983             disable : { fonts: 1 , format: 1, ..., ... , ...],
44984             btns : [ .... ]
44985         })
44986     }
44987      
44988  * 
44989  * @cfg {Object} disable List of elements to disable..
44990  * @cfg {Array} btns List of additional buttons.
44991  * 
44992  * 
44993  * NEEDS Extra CSS? 
44994  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
44995  */
44996  
44997 Roo.form.HtmlEditor.ToolbarStandard = function(config)
44998 {
44999     
45000     Roo.apply(this, config);
45001     
45002     // default disabled, based on 'good practice'..
45003     this.disable = this.disable || {};
45004     Roo.applyIf(this.disable, {
45005         fontSize : true,
45006         colors : true,
45007         specialElements : true
45008     });
45009     
45010     
45011     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45012     // dont call parent... till later.
45013 }
45014
45015 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45016     
45017     tb: false,
45018     
45019     rendered: false,
45020     
45021     editor : false,
45022     editorcore : false,
45023     /**
45024      * @cfg {Object} disable  List of toolbar elements to disable
45025          
45026      */
45027     disable : false,
45028     
45029     
45030      /**
45031      * @cfg {String} createLinkText The default text for the create link prompt
45032      */
45033     createLinkText : 'Please enter the URL for the link:',
45034     /**
45035      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45036      */
45037     defaultLinkValue : 'http:/'+'/',
45038    
45039     
45040       /**
45041      * @cfg {Array} fontFamilies An array of available font families
45042      */
45043     fontFamilies : [
45044         'Arial',
45045         'Courier New',
45046         'Tahoma',
45047         'Times New Roman',
45048         'Verdana'
45049     ],
45050     
45051     specialChars : [
45052            "&#169;",
45053           "&#174;",     
45054           "&#8482;",    
45055           "&#163;" ,    
45056          // "&#8212;",    
45057           "&#8230;",    
45058           "&#247;" ,    
45059         //  "&#225;" ,     ?? a acute?
45060            "&#8364;"    , //Euro
45061        //   "&#8220;"    ,
45062         //  "&#8221;"    ,
45063         //  "&#8226;"    ,
45064           "&#176;"  //   , // degrees
45065
45066          // "&#233;"     , // e ecute
45067          // "&#250;"     , // u ecute?
45068     ],
45069     
45070     specialElements : [
45071         {
45072             text: "Insert Table",
45073             xtype: 'MenuItem',
45074             xns : Roo.Menu,
45075             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45076                 
45077         },
45078         {    
45079             text: "Insert Image",
45080             xtype: 'MenuItem',
45081             xns : Roo.Menu,
45082             ihtml : '<img src="about:blank"/>'
45083             
45084         }
45085         
45086          
45087     ],
45088     
45089     
45090     inputElements : [ 
45091             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45092             "input:submit", "input:button", "select", "textarea", "label" ],
45093     formats : [
45094         ["p"] ,  
45095         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45096         ["pre"],[ "code"], 
45097         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45098         ['div'],['span']
45099     ],
45100     
45101     cleanStyles : [
45102         "font-size"
45103     ],
45104      /**
45105      * @cfg {String} defaultFont default font to use.
45106      */
45107     defaultFont: 'tahoma',
45108    
45109     fontSelect : false,
45110     
45111     
45112     formatCombo : false,
45113     
45114     init : function(editor)
45115     {
45116         this.editor = editor;
45117         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45118         var editorcore = this.editorcore;
45119         
45120         var _t = this;
45121         
45122         var fid = editorcore.frameId;
45123         var etb = this;
45124         function btn(id, toggle, handler){
45125             var xid = fid + '-'+ id ;
45126             return {
45127                 id : xid,
45128                 cmd : id,
45129                 cls : 'x-btn-icon x-edit-'+id,
45130                 enableToggle:toggle !== false,
45131                 scope: _t, // was editor...
45132                 handler:handler||_t.relayBtnCmd,
45133                 clickEvent:'mousedown',
45134                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45135                 tabIndex:-1
45136             };
45137         }
45138         
45139         
45140         
45141         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45142         this.tb = tb;
45143          // stop form submits
45144         tb.el.on('click', function(e){
45145             e.preventDefault(); // what does this do?
45146         });
45147
45148         if(!this.disable.font) { // && !Roo.isSafari){
45149             /* why no safari for fonts 
45150             editor.fontSelect = tb.el.createChild({
45151                 tag:'select',
45152                 tabIndex: -1,
45153                 cls:'x-font-select',
45154                 html: this.createFontOptions()
45155             });
45156             
45157             editor.fontSelect.on('change', function(){
45158                 var font = editor.fontSelect.dom.value;
45159                 editor.relayCmd('fontname', font);
45160                 editor.deferFocus();
45161             }, editor);
45162             
45163             tb.add(
45164                 editor.fontSelect.dom,
45165                 '-'
45166             );
45167             */
45168             
45169         };
45170         if(!this.disable.formats){
45171             this.formatCombo = new Roo.form.ComboBox({
45172                 store: new Roo.data.SimpleStore({
45173                     id : 'tag',
45174                     fields: ['tag'],
45175                     data : this.formats // from states.js
45176                 }),
45177                 blockFocus : true,
45178                 name : '',
45179                 //autoCreate : {tag: "div",  size: "20"},
45180                 displayField:'tag',
45181                 typeAhead: false,
45182                 mode: 'local',
45183                 editable : false,
45184                 triggerAction: 'all',
45185                 emptyText:'Add tag',
45186                 selectOnFocus:true,
45187                 width:135,
45188                 listeners : {
45189                     'select': function(c, r, i) {
45190                         editorcore.insertTag(r.get('tag'));
45191                         editor.focus();
45192                     }
45193                 }
45194
45195             });
45196             tb.addField(this.formatCombo);
45197             
45198         }
45199         
45200         if(!this.disable.format){
45201             tb.add(
45202                 btn('bold'),
45203                 btn('italic'),
45204                 btn('underline'),
45205                 btn('strikethrough')
45206             );
45207         };
45208         if(!this.disable.fontSize){
45209             tb.add(
45210                 '-',
45211                 
45212                 
45213                 btn('increasefontsize', false, editorcore.adjustFont),
45214                 btn('decreasefontsize', false, editorcore.adjustFont)
45215             );
45216         };
45217         
45218         
45219         if(!this.disable.colors){
45220             tb.add(
45221                 '-', {
45222                     id:editorcore.frameId +'-forecolor',
45223                     cls:'x-btn-icon x-edit-forecolor',
45224                     clickEvent:'mousedown',
45225                     tooltip: this.buttonTips['forecolor'] || undefined,
45226                     tabIndex:-1,
45227                     menu : new Roo.menu.ColorMenu({
45228                         allowReselect: true,
45229                         focus: Roo.emptyFn,
45230                         value:'000000',
45231                         plain:true,
45232                         selectHandler: function(cp, color){
45233                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45234                             editor.deferFocus();
45235                         },
45236                         scope: editorcore,
45237                         clickEvent:'mousedown'
45238                     })
45239                 }, {
45240                     id:editorcore.frameId +'backcolor',
45241                     cls:'x-btn-icon x-edit-backcolor',
45242                     clickEvent:'mousedown',
45243                     tooltip: this.buttonTips['backcolor'] || undefined,
45244                     tabIndex:-1,
45245                     menu : new Roo.menu.ColorMenu({
45246                         focus: Roo.emptyFn,
45247                         value:'FFFFFF',
45248                         plain:true,
45249                         allowReselect: true,
45250                         selectHandler: function(cp, color){
45251                             if(Roo.isGecko){
45252                                 editorcore.execCmd('useCSS', false);
45253                                 editorcore.execCmd('hilitecolor', color);
45254                                 editorcore.execCmd('useCSS', true);
45255                                 editor.deferFocus();
45256                             }else{
45257                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45258                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45259                                 editor.deferFocus();
45260                             }
45261                         },
45262                         scope:editorcore,
45263                         clickEvent:'mousedown'
45264                     })
45265                 }
45266             );
45267         };
45268         // now add all the items...
45269         
45270
45271         if(!this.disable.alignments){
45272             tb.add(
45273                 '-',
45274                 btn('justifyleft'),
45275                 btn('justifycenter'),
45276                 btn('justifyright')
45277             );
45278         };
45279
45280         //if(!Roo.isSafari){
45281             if(!this.disable.links){
45282                 tb.add(
45283                     '-',
45284                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45285                 );
45286             };
45287
45288             if(!this.disable.lists){
45289                 tb.add(
45290                     '-',
45291                     btn('insertorderedlist'),
45292                     btn('insertunorderedlist')
45293                 );
45294             }
45295             if(!this.disable.sourceEdit){
45296                 tb.add(
45297                     '-',
45298                     btn('sourceedit', true, function(btn){
45299                         this.toggleSourceEdit(btn.pressed);
45300                     })
45301                 );
45302             }
45303         //}
45304         
45305         var smenu = { };
45306         // special menu.. - needs to be tidied up..
45307         if (!this.disable.special) {
45308             smenu = {
45309                 text: "&#169;",
45310                 cls: 'x-edit-none',
45311                 
45312                 menu : {
45313                     items : []
45314                 }
45315             };
45316             for (var i =0; i < this.specialChars.length; i++) {
45317                 smenu.menu.items.push({
45318                     
45319                     html: this.specialChars[i],
45320                     handler: function(a,b) {
45321                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45322                         //editor.insertAtCursor(a.html);
45323                         
45324                     },
45325                     tabIndex:-1
45326                 });
45327             }
45328             
45329             
45330             tb.add(smenu);
45331             
45332             
45333         }
45334         
45335         var cmenu = { };
45336         if (!this.disable.cleanStyles) {
45337             cmenu = {
45338                 cls: 'x-btn-icon x-btn-clear',
45339                 
45340                 menu : {
45341                     items : []
45342                 }
45343             };
45344             for (var i =0; i < this.cleanStyles.length; i++) {
45345                 cmenu.menu.items.push({
45346                     actiontype : this.cleanStyles[i],
45347                     html: 'Remove ' + this.cleanStyles[i],
45348                     handler: function(a,b) {
45349 //                        Roo.log(a);
45350 //                        Roo.log(b);
45351                         var c = Roo.get(editorcore.doc.body);
45352                         c.select('[style]').each(function(s) {
45353                             s.dom.style.removeProperty(a.actiontype);
45354                         });
45355                         editorcore.syncValue();
45356                     },
45357                     tabIndex:-1
45358                 });
45359             }
45360              cmenu.menu.items.push({
45361                 actiontype : 'tablewidths',
45362                 html: 'Remove Table Widths',
45363                 handler: function(a,b) {
45364                     editorcore.cleanTableWidths();
45365                     editorcore.syncValue();
45366                 },
45367                 tabIndex:-1
45368             });
45369             cmenu.menu.items.push({
45370                 actiontype : 'word',
45371                 html: 'Remove MS Word Formating',
45372                 handler: function(a,b) {
45373                     editorcore.cleanWord();
45374                     editorcore.syncValue();
45375                 },
45376                 tabIndex:-1
45377             });
45378             
45379             cmenu.menu.items.push({
45380                 actiontype : 'all',
45381                 html: 'Remove All Styles',
45382                 handler: function(a,b) {
45383                     
45384                     var c = Roo.get(editorcore.doc.body);
45385                     c.select('[style]').each(function(s) {
45386                         s.dom.removeAttribute('style');
45387                     });
45388                     editorcore.syncValue();
45389                 },
45390                 tabIndex:-1
45391             });
45392             
45393             cmenu.menu.items.push({
45394                 actiontype : 'all',
45395                 html: 'Remove All CSS Classes',
45396                 handler: function(a,b) {
45397                     
45398                     var c = Roo.get(editorcore.doc.body);
45399                     c.select('[class]').each(function(s) {
45400                         s.dom.className = '';
45401                     });
45402                     editorcore.syncValue();
45403                 },
45404                 tabIndex:-1
45405             });
45406             
45407              cmenu.menu.items.push({
45408                 actiontype : 'tidy',
45409                 html: 'Tidy HTML Source',
45410                 handler: function(a,b) {
45411                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
45412                     editorcore.syncValue();
45413                 },
45414                 tabIndex:-1
45415             });
45416             
45417             
45418             tb.add(cmenu);
45419         }
45420          
45421         if (!this.disable.specialElements) {
45422             var semenu = {
45423                 text: "Other;",
45424                 cls: 'x-edit-none',
45425                 menu : {
45426                     items : []
45427                 }
45428             };
45429             for (var i =0; i < this.specialElements.length; i++) {
45430                 semenu.menu.items.push(
45431                     Roo.apply({ 
45432                         handler: function(a,b) {
45433                             editor.insertAtCursor(this.ihtml);
45434                         }
45435                     }, this.specialElements[i])
45436                 );
45437                     
45438             }
45439             
45440             tb.add(semenu);
45441             
45442             
45443         }
45444          
45445         
45446         if (this.btns) {
45447             for(var i =0; i< this.btns.length;i++) {
45448                 var b = Roo.factory(this.btns[i],Roo.form);
45449                 b.cls =  'x-edit-none';
45450                 
45451                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
45452                     b.cls += ' x-init-enable';
45453                 }
45454                 
45455                 b.scope = editorcore;
45456                 tb.add(b);
45457             }
45458         
45459         }
45460         
45461         
45462         
45463         // disable everything...
45464         
45465         this.tb.items.each(function(item){
45466             
45467            if(
45468                 item.id != editorcore.frameId+ '-sourceedit' && 
45469                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
45470             ){
45471                 
45472                 item.disable();
45473             }
45474         });
45475         this.rendered = true;
45476         
45477         // the all the btns;
45478         editor.on('editorevent', this.updateToolbar, this);
45479         // other toolbars need to implement this..
45480         //editor.on('editmodechange', this.updateToolbar, this);
45481     },
45482     
45483     
45484     relayBtnCmd : function(btn) {
45485         this.editorcore.relayCmd(btn.cmd);
45486     },
45487     // private used internally
45488     createLink : function(){
45489         Roo.log("create link?");
45490         var url = prompt(this.createLinkText, this.defaultLinkValue);
45491         if(url && url != 'http:/'+'/'){
45492             this.editorcore.relayCmd('createlink', url);
45493         }
45494     },
45495
45496     
45497     /**
45498      * Protected method that will not generally be called directly. It triggers
45499      * a toolbar update by reading the markup state of the current selection in the editor.
45500      */
45501     updateToolbar: function(){
45502
45503         if(!this.editorcore.activated){
45504             this.editor.onFirstFocus();
45505             return;
45506         }
45507
45508         var btns = this.tb.items.map, 
45509             doc = this.editorcore.doc,
45510             frameId = this.editorcore.frameId;
45511
45512         if(!this.disable.font && !Roo.isSafari){
45513             /*
45514             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
45515             if(name != this.fontSelect.dom.value){
45516                 this.fontSelect.dom.value = name;
45517             }
45518             */
45519         }
45520         if(!this.disable.format){
45521             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
45522             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
45523             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
45524             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
45525         }
45526         if(!this.disable.alignments){
45527             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
45528             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
45529             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
45530         }
45531         if(!Roo.isSafari && !this.disable.lists){
45532             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
45533             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
45534         }
45535         
45536         var ans = this.editorcore.getAllAncestors();
45537         if (this.formatCombo) {
45538             
45539             
45540             var store = this.formatCombo.store;
45541             this.formatCombo.setValue("");
45542             for (var i =0; i < ans.length;i++) {
45543                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
45544                     // select it..
45545                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
45546                     break;
45547                 }
45548             }
45549         }
45550         
45551         
45552         
45553         // hides menus... - so this cant be on a menu...
45554         Roo.menu.MenuMgr.hideAll();
45555
45556         //this.editorsyncValue();
45557     },
45558    
45559     
45560     createFontOptions : function(){
45561         var buf = [], fs = this.fontFamilies, ff, lc;
45562         
45563         
45564         
45565         for(var i = 0, len = fs.length; i< len; i++){
45566             ff = fs[i];
45567             lc = ff.toLowerCase();
45568             buf.push(
45569                 '<option value="',lc,'" style="font-family:',ff,';"',
45570                     (this.defaultFont == lc ? ' selected="true">' : '>'),
45571                     ff,
45572                 '</option>'
45573             );
45574         }
45575         return buf.join('');
45576     },
45577     
45578     toggleSourceEdit : function(sourceEditMode){
45579         
45580         Roo.log("toolbar toogle");
45581         if(sourceEditMode === undefined){
45582             sourceEditMode = !this.sourceEditMode;
45583         }
45584         this.sourceEditMode = sourceEditMode === true;
45585         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
45586         // just toggle the button?
45587         if(btn.pressed !== this.sourceEditMode){
45588             btn.toggle(this.sourceEditMode);
45589             return;
45590         }
45591         
45592         if(sourceEditMode){
45593             Roo.log("disabling buttons");
45594             this.tb.items.each(function(item){
45595                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
45596                     item.disable();
45597                 }
45598             });
45599           
45600         }else{
45601             Roo.log("enabling buttons");
45602             if(this.editorcore.initialized){
45603                 this.tb.items.each(function(item){
45604                     item.enable();
45605                 });
45606             }
45607             
45608         }
45609         Roo.log("calling toggole on editor");
45610         // tell the editor that it's been pressed..
45611         this.editor.toggleSourceEdit(sourceEditMode);
45612        
45613     },
45614      /**
45615      * Object collection of toolbar tooltips for the buttons in the editor. The key
45616      * is the command id associated with that button and the value is a valid QuickTips object.
45617      * For example:
45618 <pre><code>
45619 {
45620     bold : {
45621         title: 'Bold (Ctrl+B)',
45622         text: 'Make the selected text bold.',
45623         cls: 'x-html-editor-tip'
45624     },
45625     italic : {
45626         title: 'Italic (Ctrl+I)',
45627         text: 'Make the selected text italic.',
45628         cls: 'x-html-editor-tip'
45629     },
45630     ...
45631 </code></pre>
45632     * @type Object
45633      */
45634     buttonTips : {
45635         bold : {
45636             title: 'Bold (Ctrl+B)',
45637             text: 'Make the selected text bold.',
45638             cls: 'x-html-editor-tip'
45639         },
45640         italic : {
45641             title: 'Italic (Ctrl+I)',
45642             text: 'Make the selected text italic.',
45643             cls: 'x-html-editor-tip'
45644         },
45645         underline : {
45646             title: 'Underline (Ctrl+U)',
45647             text: 'Underline the selected text.',
45648             cls: 'x-html-editor-tip'
45649         },
45650         strikethrough : {
45651             title: 'Strikethrough',
45652             text: 'Strikethrough the selected text.',
45653             cls: 'x-html-editor-tip'
45654         },
45655         increasefontsize : {
45656             title: 'Grow Text',
45657             text: 'Increase the font size.',
45658             cls: 'x-html-editor-tip'
45659         },
45660         decreasefontsize : {
45661             title: 'Shrink Text',
45662             text: 'Decrease the font size.',
45663             cls: 'x-html-editor-tip'
45664         },
45665         backcolor : {
45666             title: 'Text Highlight Color',
45667             text: 'Change the background color of the selected text.',
45668             cls: 'x-html-editor-tip'
45669         },
45670         forecolor : {
45671             title: 'Font Color',
45672             text: 'Change the color of the selected text.',
45673             cls: 'x-html-editor-tip'
45674         },
45675         justifyleft : {
45676             title: 'Align Text Left',
45677             text: 'Align text to the left.',
45678             cls: 'x-html-editor-tip'
45679         },
45680         justifycenter : {
45681             title: 'Center Text',
45682             text: 'Center text in the editor.',
45683             cls: 'x-html-editor-tip'
45684         },
45685         justifyright : {
45686             title: 'Align Text Right',
45687             text: 'Align text to the right.',
45688             cls: 'x-html-editor-tip'
45689         },
45690         insertunorderedlist : {
45691             title: 'Bullet List',
45692             text: 'Start a bulleted list.',
45693             cls: 'x-html-editor-tip'
45694         },
45695         insertorderedlist : {
45696             title: 'Numbered List',
45697             text: 'Start a numbered list.',
45698             cls: 'x-html-editor-tip'
45699         },
45700         createlink : {
45701             title: 'Hyperlink',
45702             text: 'Make the selected text a hyperlink.',
45703             cls: 'x-html-editor-tip'
45704         },
45705         sourceedit : {
45706             title: 'Source Edit',
45707             text: 'Switch to source editing mode.',
45708             cls: 'x-html-editor-tip'
45709         }
45710     },
45711     // private
45712     onDestroy : function(){
45713         if(this.rendered){
45714             
45715             this.tb.items.each(function(item){
45716                 if(item.menu){
45717                     item.menu.removeAll();
45718                     if(item.menu.el){
45719                         item.menu.el.destroy();
45720                     }
45721                 }
45722                 item.destroy();
45723             });
45724              
45725         }
45726     },
45727     onFirstFocus: function() {
45728         this.tb.items.each(function(item){
45729            item.enable();
45730         });
45731     }
45732 });
45733
45734
45735
45736
45737 // <script type="text/javascript">
45738 /*
45739  * Based on
45740  * Ext JS Library 1.1.1
45741  * Copyright(c) 2006-2007, Ext JS, LLC.
45742  *  
45743  
45744  */
45745
45746  
45747 /**
45748  * @class Roo.form.HtmlEditor.ToolbarContext
45749  * Context Toolbar
45750  * 
45751  * Usage:
45752  *
45753  new Roo.form.HtmlEditor({
45754     ....
45755     toolbars : [
45756         { xtype: 'ToolbarStandard', styles : {} }
45757         { xtype: 'ToolbarContext', disable : {} }
45758     ]
45759 })
45760
45761      
45762  * 
45763  * @config : {Object} disable List of elements to disable.. (not done yet.)
45764  * @config : {Object} styles  Map of styles available.
45765  * 
45766  */
45767
45768 Roo.form.HtmlEditor.ToolbarContext = function(config)
45769 {
45770     
45771     Roo.apply(this, config);
45772     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45773     // dont call parent... till later.
45774     this.styles = this.styles || {};
45775 }
45776
45777  
45778
45779 Roo.form.HtmlEditor.ToolbarContext.types = {
45780     'IMG' : {
45781         width : {
45782             title: "Width",
45783             width: 40
45784         },
45785         height:  {
45786             title: "Height",
45787             width: 40
45788         },
45789         align: {
45790             title: "Align",
45791             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
45792             width : 80
45793             
45794         },
45795         border: {
45796             title: "Border",
45797             width: 40
45798         },
45799         alt: {
45800             title: "Alt",
45801             width: 120
45802         },
45803         src : {
45804             title: "Src",
45805             width: 220
45806         }
45807         
45808     },
45809     'A' : {
45810         name : {
45811             title: "Name",
45812             width: 50
45813         },
45814         target:  {
45815             title: "Target",
45816             width: 120
45817         },
45818         href:  {
45819             title: "Href",
45820             width: 220
45821         } // border?
45822         
45823     },
45824     'TABLE' : {
45825         rows : {
45826             title: "Rows",
45827             width: 20
45828         },
45829         cols : {
45830             title: "Cols",
45831             width: 20
45832         },
45833         width : {
45834             title: "Width",
45835             width: 40
45836         },
45837         height : {
45838             title: "Height",
45839             width: 40
45840         },
45841         border : {
45842             title: "Border",
45843             width: 20
45844         }
45845     },
45846     'TD' : {
45847         width : {
45848             title: "Width",
45849             width: 40
45850         },
45851         height : {
45852             title: "Height",
45853             width: 40
45854         },   
45855         align: {
45856             title: "Align",
45857             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
45858             width: 80
45859         },
45860         valign: {
45861             title: "Valign",
45862             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
45863             width: 80
45864         },
45865         colspan: {
45866             title: "Colspan",
45867             width: 20
45868             
45869         },
45870          'font-family'  : {
45871             title : "Font",
45872             style : 'fontFamily',
45873             displayField: 'display',
45874             optname : 'font-family',
45875             width: 140
45876         }
45877     },
45878     'INPUT' : {
45879         name : {
45880             title: "name",
45881             width: 120
45882         },
45883         value : {
45884             title: "Value",
45885             width: 120
45886         },
45887         width : {
45888             title: "Width",
45889             width: 40
45890         }
45891     },
45892     'LABEL' : {
45893         'for' : {
45894             title: "For",
45895             width: 120
45896         }
45897     },
45898     'TEXTAREA' : {
45899           name : {
45900             title: "name",
45901             width: 120
45902         },
45903         rows : {
45904             title: "Rows",
45905             width: 20
45906         },
45907         cols : {
45908             title: "Cols",
45909             width: 20
45910         }
45911     },
45912     'SELECT' : {
45913         name : {
45914             title: "name",
45915             width: 120
45916         },
45917         selectoptions : {
45918             title: "Options",
45919             width: 200
45920         }
45921     },
45922     
45923     // should we really allow this??
45924     // should this just be 
45925     'BODY' : {
45926         title : {
45927             title: "Title",
45928             width: 200,
45929             disabled : true
45930         }
45931     },
45932     'SPAN' : {
45933         'font-family'  : {
45934             title : "Font",
45935             style : 'fontFamily',
45936             displayField: 'display',
45937             optname : 'font-family',
45938             width: 140
45939         }
45940     },
45941     'DIV' : {
45942         'font-family'  : {
45943             title : "Font",
45944             style : 'fontFamily',
45945             displayField: 'display',
45946             optname : 'font-family',
45947             width: 140
45948         }
45949     },
45950      'P' : {
45951         'font-family'  : {
45952             title : "Font",
45953             style : 'fontFamily',
45954             displayField: 'display',
45955             optname : 'font-family',
45956             width: 140
45957         }
45958     },
45959     
45960     '*' : {
45961         // empty..
45962     }
45963
45964 };
45965
45966 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
45967 Roo.form.HtmlEditor.ToolbarContext.stores = false;
45968
45969 Roo.form.HtmlEditor.ToolbarContext.options = {
45970         'font-family'  : [ 
45971                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
45972                 [ 'Courier New', 'Courier New'],
45973                 [ 'Tahoma', 'Tahoma'],
45974                 [ 'Times New Roman,serif', 'Times'],
45975                 [ 'Verdana','Verdana' ]
45976         ]
45977 };
45978
45979 // fixme - these need to be configurable..
45980  
45981
45982 //Roo.form.HtmlEditor.ToolbarContext.types
45983
45984
45985 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
45986     
45987     tb: false,
45988     
45989     rendered: false,
45990     
45991     editor : false,
45992     editorcore : false,
45993     /**
45994      * @cfg {Object} disable  List of toolbar elements to disable
45995          
45996      */
45997     disable : false,
45998     /**
45999      * @cfg {Object} styles List of styles 
46000      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46001      *
46002      * These must be defined in the page, so they get rendered correctly..
46003      * .headline { }
46004      * TD.underline { }
46005      * 
46006      */
46007     styles : false,
46008     
46009     options: false,
46010     
46011     toolbars : false,
46012     
46013     init : function(editor)
46014     {
46015         this.editor = editor;
46016         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46017         var editorcore = this.editorcore;
46018         
46019         var fid = editorcore.frameId;
46020         var etb = this;
46021         function btn(id, toggle, handler){
46022             var xid = fid + '-'+ id ;
46023             return {
46024                 id : xid,
46025                 cmd : id,
46026                 cls : 'x-btn-icon x-edit-'+id,
46027                 enableToggle:toggle !== false,
46028                 scope: editorcore, // was editor...
46029                 handler:handler||editorcore.relayBtnCmd,
46030                 clickEvent:'mousedown',
46031                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46032                 tabIndex:-1
46033             };
46034         }
46035         // create a new element.
46036         var wdiv = editor.wrap.createChild({
46037                 tag: 'div'
46038             }, editor.wrap.dom.firstChild.nextSibling, true);
46039         
46040         // can we do this more than once??
46041         
46042          // stop form submits
46043       
46044  
46045         // disable everything...
46046         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46047         this.toolbars = {};
46048            
46049         for (var i in  ty) {
46050           
46051             this.toolbars[i] = this.buildToolbar(ty[i],i);
46052         }
46053         this.tb = this.toolbars.BODY;
46054         this.tb.el.show();
46055         this.buildFooter();
46056         this.footer.show();
46057         editor.on('hide', function( ) { this.footer.hide() }, this);
46058         editor.on('show', function( ) { this.footer.show() }, this);
46059         
46060          
46061         this.rendered = true;
46062         
46063         // the all the btns;
46064         editor.on('editorevent', this.updateToolbar, this);
46065         // other toolbars need to implement this..
46066         //editor.on('editmodechange', this.updateToolbar, this);
46067     },
46068     
46069     
46070     
46071     /**
46072      * Protected method that will not generally be called directly. It triggers
46073      * a toolbar update by reading the markup state of the current selection in the editor.
46074      *
46075      * Note you can force an update by calling on('editorevent', scope, false)
46076      */
46077     updateToolbar: function(editor,ev,sel){
46078
46079         //Roo.log(ev);
46080         // capture mouse up - this is handy for selecting images..
46081         // perhaps should go somewhere else...
46082         if(!this.editorcore.activated){
46083              this.editor.onFirstFocus();
46084             return;
46085         }
46086         
46087         
46088         
46089         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46090         // selectNode - might want to handle IE?
46091         if (ev &&
46092             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46093             ev.target && ev.target.tagName == 'IMG') {
46094             // they have click on an image...
46095             // let's see if we can change the selection...
46096             sel = ev.target;
46097          
46098               var nodeRange = sel.ownerDocument.createRange();
46099             try {
46100                 nodeRange.selectNode(sel);
46101             } catch (e) {
46102                 nodeRange.selectNodeContents(sel);
46103             }
46104             //nodeRange.collapse(true);
46105             var s = this.editorcore.win.getSelection();
46106             s.removeAllRanges();
46107             s.addRange(nodeRange);
46108         }  
46109         
46110       
46111         var updateFooter = sel ? false : true;
46112         
46113         
46114         var ans = this.editorcore.getAllAncestors();
46115         
46116         // pick
46117         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46118         
46119         if (!sel) { 
46120             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46121             sel = sel ? sel : this.editorcore.doc.body;
46122             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46123             
46124         }
46125         // pick a menu that exists..
46126         var tn = sel.tagName.toUpperCase();
46127         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46128         
46129         tn = sel.tagName.toUpperCase();
46130         
46131         var lastSel = this.tb.selectedNode;
46132         
46133         this.tb.selectedNode = sel;
46134         
46135         // if current menu does not match..
46136         
46137         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46138                 
46139             this.tb.el.hide();
46140             ///console.log("show: " + tn);
46141             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46142             this.tb.el.show();
46143             // update name
46144             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46145             
46146             
46147             // update attributes
46148             if (this.tb.fields) {
46149                 this.tb.fields.each(function(e) {
46150                     if (e.stylename) {
46151                         e.setValue(sel.style[e.stylename]);
46152                         return;
46153                     } 
46154                    e.setValue(sel.getAttribute(e.attrname));
46155                 });
46156             }
46157             
46158             var hasStyles = false;
46159             for(var i in this.styles) {
46160                 hasStyles = true;
46161                 break;
46162             }
46163             
46164             // update styles
46165             if (hasStyles) { 
46166                 var st = this.tb.fields.item(0);
46167                 
46168                 st.store.removeAll();
46169                
46170                 
46171                 var cn = sel.className.split(/\s+/);
46172                 
46173                 var avs = [];
46174                 if (this.styles['*']) {
46175                     
46176                     Roo.each(this.styles['*'], function(v) {
46177                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46178                     });
46179                 }
46180                 if (this.styles[tn]) { 
46181                     Roo.each(this.styles[tn], function(v) {
46182                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46183                     });
46184                 }
46185                 
46186                 st.store.loadData(avs);
46187                 st.collapse();
46188                 st.setValue(cn);
46189             }
46190             // flag our selected Node.
46191             this.tb.selectedNode = sel;
46192            
46193            
46194             Roo.menu.MenuMgr.hideAll();
46195
46196         }
46197         
46198         if (!updateFooter) {
46199             //this.footDisp.dom.innerHTML = ''; 
46200             return;
46201         }
46202         // update the footer
46203         //
46204         var html = '';
46205         
46206         this.footerEls = ans.reverse();
46207         Roo.each(this.footerEls, function(a,i) {
46208             if (!a) { return; }
46209             html += html.length ? ' &gt; '  :  '';
46210             
46211             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46212             
46213         });
46214        
46215         // 
46216         var sz = this.footDisp.up('td').getSize();
46217         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46218         this.footDisp.dom.style.marginLeft = '5px';
46219         
46220         this.footDisp.dom.style.overflow = 'hidden';
46221         
46222         this.footDisp.dom.innerHTML = html;
46223             
46224         //this.editorsyncValue();
46225     },
46226      
46227     
46228    
46229        
46230     // private
46231     onDestroy : function(){
46232         if(this.rendered){
46233             
46234             this.tb.items.each(function(item){
46235                 if(item.menu){
46236                     item.menu.removeAll();
46237                     if(item.menu.el){
46238                         item.menu.el.destroy();
46239                     }
46240                 }
46241                 item.destroy();
46242             });
46243              
46244         }
46245     },
46246     onFirstFocus: function() {
46247         // need to do this for all the toolbars..
46248         this.tb.items.each(function(item){
46249            item.enable();
46250         });
46251     },
46252     buildToolbar: function(tlist, nm)
46253     {
46254         var editor = this.editor;
46255         var editorcore = this.editorcore;
46256          // create a new element.
46257         var wdiv = editor.wrap.createChild({
46258                 tag: 'div'
46259             }, editor.wrap.dom.firstChild.nextSibling, true);
46260         
46261        
46262         var tb = new Roo.Toolbar(wdiv);
46263         // add the name..
46264         
46265         tb.add(nm+ ":&nbsp;");
46266         
46267         var styles = [];
46268         for(var i in this.styles) {
46269             styles.push(i);
46270         }
46271         
46272         // styles...
46273         if (styles && styles.length) {
46274             
46275             // this needs a multi-select checkbox...
46276             tb.addField( new Roo.form.ComboBox({
46277                 store: new Roo.data.SimpleStore({
46278                     id : 'val',
46279                     fields: ['val', 'selected'],
46280                     data : [] 
46281                 }),
46282                 name : '-roo-edit-className',
46283                 attrname : 'className',
46284                 displayField: 'val',
46285                 typeAhead: false,
46286                 mode: 'local',
46287                 editable : false,
46288                 triggerAction: 'all',
46289                 emptyText:'Select Style',
46290                 selectOnFocus:true,
46291                 width: 130,
46292                 listeners : {
46293                     'select': function(c, r, i) {
46294                         // initial support only for on class per el..
46295                         tb.selectedNode.className =  r ? r.get('val') : '';
46296                         editorcore.syncValue();
46297                     }
46298                 }
46299     
46300             }));
46301         }
46302         
46303         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46304         var tbops = tbc.options;
46305         
46306         for (var i in tlist) {
46307             
46308             var item = tlist[i];
46309             tb.add(item.title + ":&nbsp;");
46310             
46311             
46312             //optname == used so you can configure the options available..
46313             var opts = item.opts ? item.opts : false;
46314             if (item.optname) {
46315                 opts = tbops[item.optname];
46316            
46317             }
46318             
46319             if (opts) {
46320                 // opts == pulldown..
46321                 tb.addField( new Roo.form.ComboBox({
46322                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46323                         id : 'val',
46324                         fields: ['val', 'display'],
46325                         data : opts  
46326                     }),
46327                     name : '-roo-edit-' + i,
46328                     attrname : i,
46329                     stylename : item.style ? item.style : false,
46330                     displayField: item.displayField ? item.displayField : 'val',
46331                     valueField :  'val',
46332                     typeAhead: false,
46333                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46334                     editable : false,
46335                     triggerAction: 'all',
46336                     emptyText:'Select',
46337                     selectOnFocus:true,
46338                     width: item.width ? item.width  : 130,
46339                     listeners : {
46340                         'select': function(c, r, i) {
46341                             if (c.stylename) {
46342                                 tb.selectedNode.style[c.stylename] =  r.get('val');
46343                                 return;
46344                             }
46345                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
46346                         }
46347                     }
46348
46349                 }));
46350                 continue;
46351                     
46352                  
46353                 
46354                 tb.addField( new Roo.form.TextField({
46355                     name: i,
46356                     width: 100,
46357                     //allowBlank:false,
46358                     value: ''
46359                 }));
46360                 continue;
46361             }
46362             tb.addField( new Roo.form.TextField({
46363                 name: '-roo-edit-' + i,
46364                 attrname : i,
46365                 
46366                 width: item.width,
46367                 //allowBlank:true,
46368                 value: '',
46369                 listeners: {
46370                     'change' : function(f, nv, ov) {
46371                         tb.selectedNode.setAttribute(f.attrname, nv);
46372                         editorcore.syncValue();
46373                     }
46374                 }
46375             }));
46376              
46377         }
46378         
46379         var _this = this;
46380         
46381         if(nm == 'BODY'){
46382             tb.addSeparator();
46383         
46384             tb.addButton( {
46385                 text: 'Stylesheets',
46386
46387                 listeners : {
46388                     click : function ()
46389                     {
46390                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
46391                     }
46392                 }
46393             });
46394         }
46395         
46396         tb.addFill();
46397         tb.addButton( {
46398             text: 'Remove Tag',
46399     
46400             listeners : {
46401                 click : function ()
46402                 {
46403                     // remove
46404                     // undo does not work.
46405                      
46406                     var sn = tb.selectedNode;
46407                     
46408                     var pn = sn.parentNode;
46409                     
46410                     var stn =  sn.childNodes[0];
46411                     var en = sn.childNodes[sn.childNodes.length - 1 ];
46412                     while (sn.childNodes.length) {
46413                         var node = sn.childNodes[0];
46414                         sn.removeChild(node);
46415                         //Roo.log(node);
46416                         pn.insertBefore(node, sn);
46417                         
46418                     }
46419                     pn.removeChild(sn);
46420                     var range = editorcore.createRange();
46421         
46422                     range.setStart(stn,0);
46423                     range.setEnd(en,0); //????
46424                     //range.selectNode(sel);
46425                     
46426                     
46427                     var selection = editorcore.getSelection();
46428                     selection.removeAllRanges();
46429                     selection.addRange(range);
46430                     
46431                     
46432                     
46433                     //_this.updateToolbar(null, null, pn);
46434                     _this.updateToolbar(null, null, null);
46435                     _this.footDisp.dom.innerHTML = ''; 
46436                 }
46437             }
46438             
46439                     
46440                 
46441             
46442         });
46443         
46444         
46445         tb.el.on('click', function(e){
46446             e.preventDefault(); // what does this do?
46447         });
46448         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
46449         tb.el.hide();
46450         tb.name = nm;
46451         // dont need to disable them... as they will get hidden
46452         return tb;
46453          
46454         
46455     },
46456     buildFooter : function()
46457     {
46458         
46459         var fel = this.editor.wrap.createChild();
46460         this.footer = new Roo.Toolbar(fel);
46461         // toolbar has scrolly on left / right?
46462         var footDisp= new Roo.Toolbar.Fill();
46463         var _t = this;
46464         this.footer.add(
46465             {
46466                 text : '&lt;',
46467                 xtype: 'Button',
46468                 handler : function() {
46469                     _t.footDisp.scrollTo('left',0,true)
46470                 }
46471             }
46472         );
46473         this.footer.add( footDisp );
46474         this.footer.add( 
46475             {
46476                 text : '&gt;',
46477                 xtype: 'Button',
46478                 handler : function() {
46479                     // no animation..
46480                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
46481                 }
46482             }
46483         );
46484         var fel = Roo.get(footDisp.el);
46485         fel.addClass('x-editor-context');
46486         this.footDispWrap = fel; 
46487         this.footDispWrap.overflow  = 'hidden';
46488         
46489         this.footDisp = fel.createChild();
46490         this.footDispWrap.on('click', this.onContextClick, this)
46491         
46492         
46493     },
46494     onContextClick : function (ev,dom)
46495     {
46496         ev.preventDefault();
46497         var  cn = dom.className;
46498         //Roo.log(cn);
46499         if (!cn.match(/x-ed-loc-/)) {
46500             return;
46501         }
46502         var n = cn.split('-').pop();
46503         var ans = this.footerEls;
46504         var sel = ans[n];
46505         
46506          // pick
46507         var range = this.editorcore.createRange();
46508         
46509         range.selectNodeContents(sel);
46510         //range.selectNode(sel);
46511         
46512         
46513         var selection = this.editorcore.getSelection();
46514         selection.removeAllRanges();
46515         selection.addRange(range);
46516         
46517         
46518         
46519         this.updateToolbar(null, null, sel);
46520         
46521         
46522     }
46523     
46524     
46525     
46526     
46527     
46528 });
46529
46530
46531
46532
46533
46534 /*
46535  * Based on:
46536  * Ext JS Library 1.1.1
46537  * Copyright(c) 2006-2007, Ext JS, LLC.
46538  *
46539  * Originally Released Under LGPL - original licence link has changed is not relivant.
46540  *
46541  * Fork - LGPL
46542  * <script type="text/javascript">
46543  */
46544  
46545 /**
46546  * @class Roo.form.BasicForm
46547  * @extends Roo.util.Observable
46548  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
46549  * @constructor
46550  * @param {String/HTMLElement/Roo.Element} el The form element or its id
46551  * @param {Object} config Configuration options
46552  */
46553 Roo.form.BasicForm = function(el, config){
46554     this.allItems = [];
46555     this.childForms = [];
46556     Roo.apply(this, config);
46557     /*
46558      * The Roo.form.Field items in this form.
46559      * @type MixedCollection
46560      */
46561      
46562      
46563     this.items = new Roo.util.MixedCollection(false, function(o){
46564         return o.id || (o.id = Roo.id());
46565     });
46566     this.addEvents({
46567         /**
46568          * @event beforeaction
46569          * Fires before any action is performed. Return false to cancel the action.
46570          * @param {Form} this
46571          * @param {Action} action The action to be performed
46572          */
46573         beforeaction: true,
46574         /**
46575          * @event actionfailed
46576          * Fires when an action fails.
46577          * @param {Form} this
46578          * @param {Action} action The action that failed
46579          */
46580         actionfailed : true,
46581         /**
46582          * @event actioncomplete
46583          * Fires when an action is completed.
46584          * @param {Form} this
46585          * @param {Action} action The action that completed
46586          */
46587         actioncomplete : true
46588     });
46589     if(el){
46590         this.initEl(el);
46591     }
46592     Roo.form.BasicForm.superclass.constructor.call(this);
46593 };
46594
46595 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
46596     /**
46597      * @cfg {String} method
46598      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
46599      */
46600     /**
46601      * @cfg {DataReader} reader
46602      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
46603      * This is optional as there is built-in support for processing JSON.
46604      */
46605     /**
46606      * @cfg {DataReader} errorReader
46607      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
46608      * This is completely optional as there is built-in support for processing JSON.
46609      */
46610     /**
46611      * @cfg {String} url
46612      * The URL to use for form actions if one isn't supplied in the action options.
46613      */
46614     /**
46615      * @cfg {Boolean} fileUpload
46616      * Set to true if this form is a file upload.
46617      */
46618      
46619     /**
46620      * @cfg {Object} baseParams
46621      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
46622      */
46623      /**
46624      
46625     /**
46626      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
46627      */
46628     timeout: 30,
46629
46630     // private
46631     activeAction : null,
46632
46633     /**
46634      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
46635      * or setValues() data instead of when the form was first created.
46636      */
46637     trackResetOnLoad : false,
46638     
46639     
46640     /**
46641      * childForms - used for multi-tab forms
46642      * @type {Array}
46643      */
46644     childForms : false,
46645     
46646     /**
46647      * allItems - full list of fields.
46648      * @type {Array}
46649      */
46650     allItems : false,
46651     
46652     /**
46653      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
46654      * element by passing it or its id or mask the form itself by passing in true.
46655      * @type Mixed
46656      */
46657     waitMsgTarget : false,
46658
46659     // private
46660     initEl : function(el){
46661         this.el = Roo.get(el);
46662         this.id = this.el.id || Roo.id();
46663         this.el.on('submit', this.onSubmit, this);
46664         this.el.addClass('x-form');
46665     },
46666
46667     // private
46668     onSubmit : function(e){
46669         e.stopEvent();
46670     },
46671
46672     /**
46673      * Returns true if client-side validation on the form is successful.
46674      * @return Boolean
46675      */
46676     isValid : function(){
46677         var valid = true;
46678         this.items.each(function(f){
46679            if(!f.validate()){
46680                valid = false;
46681            }
46682         });
46683         return valid;
46684     },
46685
46686     /**
46687      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
46688      * @return Boolean
46689      */
46690     isDirty : function(){
46691         var dirty = false;
46692         this.items.each(function(f){
46693            if(f.isDirty()){
46694                dirty = true;
46695                return false;
46696            }
46697         });
46698         return dirty;
46699     },
46700     
46701     /**
46702      * Returns true if any fields in this form have changed since their original load. (New version)
46703      * @return Boolean
46704      */
46705     
46706     hasChanged : function()
46707     {
46708         var dirty = false;
46709         this.items.each(function(f){
46710            if(f.hasChanged()){
46711                dirty = true;
46712                return false;
46713            }
46714         });
46715         return dirty;
46716         
46717     },
46718     /**
46719      * Resets all hasChanged to 'false' -
46720      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
46721      * So hasChanged storage is only to be used for this purpose
46722      * @return Boolean
46723      */
46724     resetHasChanged : function()
46725     {
46726         this.items.each(function(f){
46727            f.resetHasChanged();
46728         });
46729         
46730     },
46731     
46732     
46733     /**
46734      * Performs a predefined action (submit or load) or custom actions you define on this form.
46735      * @param {String} actionName The name of the action type
46736      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
46737      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
46738      * accept other config options):
46739      * <pre>
46740 Property          Type             Description
46741 ----------------  ---------------  ----------------------------------------------------------------------------------
46742 url               String           The url for the action (defaults to the form's url)
46743 method            String           The form method to use (defaults to the form's method, or POST if not defined)
46744 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
46745 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
46746                                    validate the form on the client (defaults to false)
46747      * </pre>
46748      * @return {BasicForm} this
46749      */
46750     doAction : function(action, options){
46751         if(typeof action == 'string'){
46752             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
46753         }
46754         if(this.fireEvent('beforeaction', this, action) !== false){
46755             this.beforeAction(action);
46756             action.run.defer(100, action);
46757         }
46758         return this;
46759     },
46760
46761     /**
46762      * Shortcut to do a submit action.
46763      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46764      * @return {BasicForm} this
46765      */
46766     submit : function(options){
46767         this.doAction('submit', options);
46768         return this;
46769     },
46770
46771     /**
46772      * Shortcut to do a load action.
46773      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
46774      * @return {BasicForm} this
46775      */
46776     load : function(options){
46777         this.doAction('load', options);
46778         return this;
46779     },
46780
46781     /**
46782      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
46783      * @param {Record} record The record to edit
46784      * @return {BasicForm} this
46785      */
46786     updateRecord : function(record){
46787         record.beginEdit();
46788         var fs = record.fields;
46789         fs.each(function(f){
46790             var field = this.findField(f.name);
46791             if(field){
46792                 record.set(f.name, field.getValue());
46793             }
46794         }, this);
46795         record.endEdit();
46796         return this;
46797     },
46798
46799     /**
46800      * Loads an Roo.data.Record into this form.
46801      * @param {Record} record The record to load
46802      * @return {BasicForm} this
46803      */
46804     loadRecord : function(record){
46805         this.setValues(record.data);
46806         return this;
46807     },
46808
46809     // private
46810     beforeAction : function(action){
46811         var o = action.options;
46812         
46813        
46814         if(this.waitMsgTarget === true){
46815             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
46816         }else if(this.waitMsgTarget){
46817             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
46818             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
46819         }else {
46820             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
46821         }
46822          
46823     },
46824
46825     // private
46826     afterAction : function(action, success){
46827         this.activeAction = null;
46828         var o = action.options;
46829         
46830         if(this.waitMsgTarget === true){
46831             this.el.unmask();
46832         }else if(this.waitMsgTarget){
46833             this.waitMsgTarget.unmask();
46834         }else{
46835             Roo.MessageBox.updateProgress(1);
46836             Roo.MessageBox.hide();
46837         }
46838          
46839         if(success){
46840             if(o.reset){
46841                 this.reset();
46842             }
46843             Roo.callback(o.success, o.scope, [this, action]);
46844             this.fireEvent('actioncomplete', this, action);
46845             
46846         }else{
46847             
46848             // failure condition..
46849             // we have a scenario where updates need confirming.
46850             // eg. if a locking scenario exists..
46851             // we look for { errors : { needs_confirm : true }} in the response.
46852             if (
46853                 (typeof(action.result) != 'undefined')  &&
46854                 (typeof(action.result.errors) != 'undefined')  &&
46855                 (typeof(action.result.errors.needs_confirm) != 'undefined')
46856            ){
46857                 var _t = this;
46858                 Roo.MessageBox.confirm(
46859                     "Change requires confirmation",
46860                     action.result.errorMsg,
46861                     function(r) {
46862                         if (r != 'yes') {
46863                             return;
46864                         }
46865                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
46866                     }
46867                     
46868                 );
46869                 
46870                 
46871                 
46872                 return;
46873             }
46874             
46875             Roo.callback(o.failure, o.scope, [this, action]);
46876             // show an error message if no failed handler is set..
46877             if (!this.hasListener('actionfailed')) {
46878                 Roo.MessageBox.alert("Error",
46879                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
46880                         action.result.errorMsg :
46881                         "Saving Failed, please check your entries or try again"
46882                 );
46883             }
46884             
46885             this.fireEvent('actionfailed', this, action);
46886         }
46887         
46888     },
46889
46890     /**
46891      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
46892      * @param {String} id The value to search for
46893      * @return Field
46894      */
46895     findField : function(id){
46896         var field = this.items.get(id);
46897         if(!field){
46898             this.items.each(function(f){
46899                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
46900                     field = f;
46901                     return false;
46902                 }
46903             });
46904         }
46905         return field || null;
46906     },
46907
46908     /**
46909      * Add a secondary form to this one, 
46910      * Used to provide tabbed forms. One form is primary, with hidden values 
46911      * which mirror the elements from the other forms.
46912      * 
46913      * @param {Roo.form.Form} form to add.
46914      * 
46915      */
46916     addForm : function(form)
46917     {
46918        
46919         if (this.childForms.indexOf(form) > -1) {
46920             // already added..
46921             return;
46922         }
46923         this.childForms.push(form);
46924         var n = '';
46925         Roo.each(form.allItems, function (fe) {
46926             
46927             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
46928             if (this.findField(n)) { // already added..
46929                 return;
46930             }
46931             var add = new Roo.form.Hidden({
46932                 name : n
46933             });
46934             add.render(this.el);
46935             
46936             this.add( add );
46937         }, this);
46938         
46939     },
46940     /**
46941      * Mark fields in this form invalid in bulk.
46942      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
46943      * @return {BasicForm} this
46944      */
46945     markInvalid : function(errors){
46946         if(errors instanceof Array){
46947             for(var i = 0, len = errors.length; i < len; i++){
46948                 var fieldError = errors[i];
46949                 var f = this.findField(fieldError.id);
46950                 if(f){
46951                     f.markInvalid(fieldError.msg);
46952                 }
46953             }
46954         }else{
46955             var field, id;
46956             for(id in errors){
46957                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
46958                     field.markInvalid(errors[id]);
46959                 }
46960             }
46961         }
46962         Roo.each(this.childForms || [], function (f) {
46963             f.markInvalid(errors);
46964         });
46965         
46966         return this;
46967     },
46968
46969     /**
46970      * Set values for fields in this form in bulk.
46971      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
46972      * @return {BasicForm} this
46973      */
46974     setValues : function(values){
46975         if(values instanceof Array){ // array of objects
46976             for(var i = 0, len = values.length; i < len; i++){
46977                 var v = values[i];
46978                 var f = this.findField(v.id);
46979                 if(f){
46980                     f.setValue(v.value);
46981                     if(this.trackResetOnLoad){
46982                         f.originalValue = f.getValue();
46983                     }
46984                 }
46985             }
46986         }else{ // object hash
46987             var field, id;
46988             for(id in values){
46989                 if(typeof values[id] != 'function' && (field = this.findField(id))){
46990                     
46991                     if (field.setFromData && 
46992                         field.valueField && 
46993                         field.displayField &&
46994                         // combos' with local stores can 
46995                         // be queried via setValue()
46996                         // to set their value..
46997                         (field.store && !field.store.isLocal)
46998                         ) {
46999                         // it's a combo
47000                         var sd = { };
47001                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47002                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47003                         field.setFromData(sd);
47004                         
47005                     } else {
47006                         field.setValue(values[id]);
47007                     }
47008                     
47009                     
47010                     if(this.trackResetOnLoad){
47011                         field.originalValue = field.getValue();
47012                     }
47013                 }
47014             }
47015         }
47016         this.resetHasChanged();
47017         
47018         
47019         Roo.each(this.childForms || [], function (f) {
47020             f.setValues(values);
47021             f.resetHasChanged();
47022         });
47023                 
47024         return this;
47025     },
47026
47027     /**
47028      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47029      * they are returned as an array.
47030      * @param {Boolean} asString
47031      * @return {Object}
47032      */
47033     getValues : function(asString){
47034         if (this.childForms) {
47035             // copy values from the child forms
47036             Roo.each(this.childForms, function (f) {
47037                 this.setValues(f.getValues());
47038             }, this);
47039         }
47040         
47041         
47042         
47043         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47044         if(asString === true){
47045             return fs;
47046         }
47047         return Roo.urlDecode(fs);
47048     },
47049     
47050     /**
47051      * Returns the fields in this form as an object with key/value pairs. 
47052      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47053      * @return {Object}
47054      */
47055     getFieldValues : function(with_hidden)
47056     {
47057         if (this.childForms) {
47058             // copy values from the child forms
47059             // should this call getFieldValues - probably not as we do not currently copy
47060             // hidden fields when we generate..
47061             Roo.each(this.childForms, function (f) {
47062                 this.setValues(f.getValues());
47063             }, this);
47064         }
47065         
47066         var ret = {};
47067         this.items.each(function(f){
47068             if (!f.getName()) {
47069                 return;
47070             }
47071             var v = f.getValue();
47072             if (f.inputType =='radio') {
47073                 if (typeof(ret[f.getName()]) == 'undefined') {
47074                     ret[f.getName()] = ''; // empty..
47075                 }
47076                 
47077                 if (!f.el.dom.checked) {
47078                     return;
47079                     
47080                 }
47081                 v = f.el.dom.value;
47082                 
47083             }
47084             
47085             // not sure if this supported any more..
47086             if ((typeof(v) == 'object') && f.getRawValue) {
47087                 v = f.getRawValue() ; // dates..
47088             }
47089             // combo boxes where name != hiddenName...
47090             if (f.name != f.getName()) {
47091                 ret[f.name] = f.getRawValue();
47092             }
47093             ret[f.getName()] = v;
47094         });
47095         
47096         return ret;
47097     },
47098
47099     /**
47100      * Clears all invalid messages in this form.
47101      * @return {BasicForm} this
47102      */
47103     clearInvalid : function(){
47104         this.items.each(function(f){
47105            f.clearInvalid();
47106         });
47107         
47108         Roo.each(this.childForms || [], function (f) {
47109             f.clearInvalid();
47110         });
47111         
47112         
47113         return this;
47114     },
47115
47116     /**
47117      * Resets this form.
47118      * @return {BasicForm} this
47119      */
47120     reset : function(){
47121         this.items.each(function(f){
47122             f.reset();
47123         });
47124         
47125         Roo.each(this.childForms || [], function (f) {
47126             f.reset();
47127         });
47128         this.resetHasChanged();
47129         
47130         return this;
47131     },
47132
47133     /**
47134      * Add Roo.form components to this form.
47135      * @param {Field} field1
47136      * @param {Field} field2 (optional)
47137      * @param {Field} etc (optional)
47138      * @return {BasicForm} this
47139      */
47140     add : function(){
47141         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47142         return this;
47143     },
47144
47145
47146     /**
47147      * Removes a field from the items collection (does NOT remove its markup).
47148      * @param {Field} field
47149      * @return {BasicForm} this
47150      */
47151     remove : function(field){
47152         this.items.remove(field);
47153         return this;
47154     },
47155
47156     /**
47157      * Looks at the fields in this form, checks them for an id attribute,
47158      * and calls applyTo on the existing dom element with that id.
47159      * @return {BasicForm} this
47160      */
47161     render : function(){
47162         this.items.each(function(f){
47163             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47164                 f.applyTo(f.id);
47165             }
47166         });
47167         return this;
47168     },
47169
47170     /**
47171      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47172      * @param {Object} values
47173      * @return {BasicForm} this
47174      */
47175     applyToFields : function(o){
47176         this.items.each(function(f){
47177            Roo.apply(f, o);
47178         });
47179         return this;
47180     },
47181
47182     /**
47183      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47184      * @param {Object} values
47185      * @return {BasicForm} this
47186      */
47187     applyIfToFields : function(o){
47188         this.items.each(function(f){
47189            Roo.applyIf(f, o);
47190         });
47191         return this;
47192     }
47193 });
47194
47195 // back compat
47196 Roo.BasicForm = Roo.form.BasicForm;/*
47197  * Based on:
47198  * Ext JS Library 1.1.1
47199  * Copyright(c) 2006-2007, Ext JS, LLC.
47200  *
47201  * Originally Released Under LGPL - original licence link has changed is not relivant.
47202  *
47203  * Fork - LGPL
47204  * <script type="text/javascript">
47205  */
47206
47207 /**
47208  * @class Roo.form.Form
47209  * @extends Roo.form.BasicForm
47210  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
47211  * @constructor
47212  * @param {Object} config Configuration options
47213  */
47214 Roo.form.Form = function(config){
47215     var xitems =  [];
47216     if (config.items) {
47217         xitems = config.items;
47218         delete config.items;
47219     }
47220    
47221     
47222     Roo.form.Form.superclass.constructor.call(this, null, config);
47223     this.url = this.url || this.action;
47224     if(!this.root){
47225         this.root = new Roo.form.Layout(Roo.applyIf({
47226             id: Roo.id()
47227         }, config));
47228     }
47229     this.active = this.root;
47230     /**
47231      * Array of all the buttons that have been added to this form via {@link addButton}
47232      * @type Array
47233      */
47234     this.buttons = [];
47235     this.allItems = [];
47236     this.addEvents({
47237         /**
47238          * @event clientvalidation
47239          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
47240          * @param {Form} this
47241          * @param {Boolean} valid true if the form has passed client-side validation
47242          */
47243         clientvalidation: true,
47244         /**
47245          * @event rendered
47246          * Fires when the form is rendered
47247          * @param {Roo.form.Form} form
47248          */
47249         rendered : true
47250     });
47251     
47252     if (this.progressUrl) {
47253             // push a hidden field onto the list of fields..
47254             this.addxtype( {
47255                     xns: Roo.form, 
47256                     xtype : 'Hidden', 
47257                     name : 'UPLOAD_IDENTIFIER' 
47258             });
47259         }
47260         
47261     
47262     Roo.each(xitems, this.addxtype, this);
47263     
47264     
47265     
47266 };
47267
47268 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
47269     /**
47270      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
47271      */
47272     /**
47273      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
47274      */
47275     /**
47276      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
47277      */
47278     buttonAlign:'center',
47279
47280     /**
47281      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
47282      */
47283     minButtonWidth:75,
47284
47285     /**
47286      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
47287      * This property cascades to child containers if not set.
47288      */
47289     labelAlign:'left',
47290
47291     /**
47292      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
47293      * fires a looping event with that state. This is required to bind buttons to the valid
47294      * state using the config value formBind:true on the button.
47295      */
47296     monitorValid : false,
47297
47298     /**
47299      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
47300      */
47301     monitorPoll : 200,
47302     
47303     /**
47304      * @cfg {String} progressUrl - Url to return progress data 
47305      */
47306     
47307     progressUrl : false,
47308   
47309     /**
47310      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
47311      * fields are added and the column is closed. If no fields are passed the column remains open
47312      * until end() is called.
47313      * @param {Object} config The config to pass to the column
47314      * @param {Field} field1 (optional)
47315      * @param {Field} field2 (optional)
47316      * @param {Field} etc (optional)
47317      * @return Column The column container object
47318      */
47319     column : function(c){
47320         var col = new Roo.form.Column(c);
47321         this.start(col);
47322         if(arguments.length > 1){ // duplicate code required because of Opera
47323             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47324             this.end();
47325         }
47326         return col;
47327     },
47328
47329     /**
47330      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
47331      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
47332      * until end() is called.
47333      * @param {Object} config The config to pass to the fieldset
47334      * @param {Field} field1 (optional)
47335      * @param {Field} field2 (optional)
47336      * @param {Field} etc (optional)
47337      * @return FieldSet The fieldset container object
47338      */
47339     fieldset : function(c){
47340         var fs = new Roo.form.FieldSet(c);
47341         this.start(fs);
47342         if(arguments.length > 1){ // duplicate code required because of Opera
47343             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47344             this.end();
47345         }
47346         return fs;
47347     },
47348
47349     /**
47350      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
47351      * fields are added and the container is closed. If no fields are passed the container remains open
47352      * until end() is called.
47353      * @param {Object} config The config to pass to the Layout
47354      * @param {Field} field1 (optional)
47355      * @param {Field} field2 (optional)
47356      * @param {Field} etc (optional)
47357      * @return Layout The container object
47358      */
47359     container : function(c){
47360         var l = new Roo.form.Layout(c);
47361         this.start(l);
47362         if(arguments.length > 1){ // duplicate code required because of Opera
47363             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
47364             this.end();
47365         }
47366         return l;
47367     },
47368
47369     /**
47370      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
47371      * @param {Object} container A Roo.form.Layout or subclass of Layout
47372      * @return {Form} this
47373      */
47374     start : function(c){
47375         // cascade label info
47376         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
47377         this.active.stack.push(c);
47378         c.ownerCt = this.active;
47379         this.active = c;
47380         return this;
47381     },
47382
47383     /**
47384      * Closes the current open container
47385      * @return {Form} this
47386      */
47387     end : function(){
47388         if(this.active == this.root){
47389             return this;
47390         }
47391         this.active = this.active.ownerCt;
47392         return this;
47393     },
47394
47395     /**
47396      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
47397      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
47398      * as the label of the field.
47399      * @param {Field} field1
47400      * @param {Field} field2 (optional)
47401      * @param {Field} etc. (optional)
47402      * @return {Form} this
47403      */
47404     add : function(){
47405         this.active.stack.push.apply(this.active.stack, arguments);
47406         this.allItems.push.apply(this.allItems,arguments);
47407         var r = [];
47408         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
47409             if(a[i].isFormField){
47410                 r.push(a[i]);
47411             }
47412         }
47413         if(r.length > 0){
47414             Roo.form.Form.superclass.add.apply(this, r);
47415         }
47416         return this;
47417     },
47418     
47419
47420     
47421     
47422     
47423      /**
47424      * Find any element that has been added to a form, using it's ID or name
47425      * This can include framesets, columns etc. along with regular fields..
47426      * @param {String} id - id or name to find.
47427      
47428      * @return {Element} e - or false if nothing found.
47429      */
47430     findbyId : function(id)
47431     {
47432         var ret = false;
47433         if (!id) {
47434             return ret;
47435         }
47436         Roo.each(this.allItems, function(f){
47437             if (f.id == id || f.name == id ){
47438                 ret = f;
47439                 return false;
47440             }
47441         });
47442         return ret;
47443     },
47444
47445     
47446     
47447     /**
47448      * Render this form into the passed container. This should only be called once!
47449      * @param {String/HTMLElement/Element} container The element this component should be rendered into
47450      * @return {Form} this
47451      */
47452     render : function(ct)
47453     {
47454         
47455         
47456         
47457         ct = Roo.get(ct);
47458         var o = this.autoCreate || {
47459             tag: 'form',
47460             method : this.method || 'POST',
47461             id : this.id || Roo.id()
47462         };
47463         this.initEl(ct.createChild(o));
47464
47465         this.root.render(this.el);
47466         
47467        
47468              
47469         this.items.each(function(f){
47470             f.render('x-form-el-'+f.id);
47471         });
47472
47473         if(this.buttons.length > 0){
47474             // tables are required to maintain order and for correct IE layout
47475             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
47476                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
47477                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
47478             }}, null, true);
47479             var tr = tb.getElementsByTagName('tr')[0];
47480             for(var i = 0, len = this.buttons.length; i < len; i++) {
47481                 var b = this.buttons[i];
47482                 var td = document.createElement('td');
47483                 td.className = 'x-form-btn-td';
47484                 b.render(tr.appendChild(td));
47485             }
47486         }
47487         if(this.monitorValid){ // initialize after render
47488             this.startMonitoring();
47489         }
47490         this.fireEvent('rendered', this);
47491         return this;
47492     },
47493
47494     /**
47495      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
47496      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
47497      * object or a valid Roo.DomHelper element config
47498      * @param {Function} handler The function called when the button is clicked
47499      * @param {Object} scope (optional) The scope of the handler function
47500      * @return {Roo.Button}
47501      */
47502     addButton : function(config, handler, scope){
47503         var bc = {
47504             handler: handler,
47505             scope: scope,
47506             minWidth: this.minButtonWidth,
47507             hideParent:true
47508         };
47509         if(typeof config == "string"){
47510             bc.text = config;
47511         }else{
47512             Roo.apply(bc, config);
47513         }
47514         var btn = new Roo.Button(null, bc);
47515         this.buttons.push(btn);
47516         return btn;
47517     },
47518
47519      /**
47520      * Adds a series of form elements (using the xtype property as the factory method.
47521      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
47522      * @param {Object} config 
47523      */
47524     
47525     addxtype : function()
47526     {
47527         var ar = Array.prototype.slice.call(arguments, 0);
47528         var ret = false;
47529         for(var i = 0; i < ar.length; i++) {
47530             if (!ar[i]) {
47531                 continue; // skip -- if this happends something invalid got sent, we 
47532                 // should ignore it, as basically that interface element will not show up
47533                 // and that should be pretty obvious!!
47534             }
47535             
47536             if (Roo.form[ar[i].xtype]) {
47537                 ar[i].form = this;
47538                 var fe = Roo.factory(ar[i], Roo.form);
47539                 if (!ret) {
47540                     ret = fe;
47541                 }
47542                 fe.form = this;
47543                 if (fe.store) {
47544                     fe.store.form = this;
47545                 }
47546                 if (fe.isLayout) {  
47547                          
47548                     this.start(fe);
47549                     this.allItems.push(fe);
47550                     if (fe.items && fe.addxtype) {
47551                         fe.addxtype.apply(fe, fe.items);
47552                         delete fe.items;
47553                     }
47554                      this.end();
47555                     continue;
47556                 }
47557                 
47558                 
47559                  
47560                 this.add(fe);
47561               //  console.log('adding ' + ar[i].xtype);
47562             }
47563             if (ar[i].xtype == 'Button') {  
47564                 //console.log('adding button');
47565                 //console.log(ar[i]);
47566                 this.addButton(ar[i]);
47567                 this.allItems.push(fe);
47568                 continue;
47569             }
47570             
47571             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
47572                 alert('end is not supported on xtype any more, use items');
47573             //    this.end();
47574             //    //console.log('adding end');
47575             }
47576             
47577         }
47578         return ret;
47579     },
47580     
47581     /**
47582      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
47583      * option "monitorValid"
47584      */
47585     startMonitoring : function(){
47586         if(!this.bound){
47587             this.bound = true;
47588             Roo.TaskMgr.start({
47589                 run : this.bindHandler,
47590                 interval : this.monitorPoll || 200,
47591                 scope: this
47592             });
47593         }
47594     },
47595
47596     /**
47597      * Stops monitoring of the valid state of this form
47598      */
47599     stopMonitoring : function(){
47600         this.bound = false;
47601     },
47602
47603     // private
47604     bindHandler : function(){
47605         if(!this.bound){
47606             return false; // stops binding
47607         }
47608         var valid = true;
47609         this.items.each(function(f){
47610             if(!f.isValid(true)){
47611                 valid = false;
47612                 return false;
47613             }
47614         });
47615         for(var i = 0, len = this.buttons.length; i < len; i++){
47616             var btn = this.buttons[i];
47617             if(btn.formBind === true && btn.disabled === valid){
47618                 btn.setDisabled(!valid);
47619             }
47620         }
47621         this.fireEvent('clientvalidation', this, valid);
47622     }
47623     
47624     
47625     
47626     
47627     
47628     
47629     
47630     
47631 });
47632
47633
47634 // back compat
47635 Roo.Form = Roo.form.Form;
47636 /*
47637  * Based on:
47638  * Ext JS Library 1.1.1
47639  * Copyright(c) 2006-2007, Ext JS, LLC.
47640  *
47641  * Originally Released Under LGPL - original licence link has changed is not relivant.
47642  *
47643  * Fork - LGPL
47644  * <script type="text/javascript">
47645  */
47646
47647 // as we use this in bootstrap.
47648 Roo.namespace('Roo.form');
47649  /**
47650  * @class Roo.form.Action
47651  * Internal Class used to handle form actions
47652  * @constructor
47653  * @param {Roo.form.BasicForm} el The form element or its id
47654  * @param {Object} config Configuration options
47655  */
47656
47657  
47658  
47659 // define the action interface
47660 Roo.form.Action = function(form, options){
47661     this.form = form;
47662     this.options = options || {};
47663 };
47664 /**
47665  * Client Validation Failed
47666  * @const 
47667  */
47668 Roo.form.Action.CLIENT_INVALID = 'client';
47669 /**
47670  * Server Validation Failed
47671  * @const 
47672  */
47673 Roo.form.Action.SERVER_INVALID = 'server';
47674  /**
47675  * Connect to Server Failed
47676  * @const 
47677  */
47678 Roo.form.Action.CONNECT_FAILURE = 'connect';
47679 /**
47680  * Reading Data from Server Failed
47681  * @const 
47682  */
47683 Roo.form.Action.LOAD_FAILURE = 'load';
47684
47685 Roo.form.Action.prototype = {
47686     type : 'default',
47687     failureType : undefined,
47688     response : undefined,
47689     result : undefined,
47690
47691     // interface method
47692     run : function(options){
47693
47694     },
47695
47696     // interface method
47697     success : function(response){
47698
47699     },
47700
47701     // interface method
47702     handleResponse : function(response){
47703
47704     },
47705
47706     // default connection failure
47707     failure : function(response){
47708         
47709         this.response = response;
47710         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47711         this.form.afterAction(this, false);
47712     },
47713
47714     processResponse : function(response){
47715         this.response = response;
47716         if(!response.responseText){
47717             return true;
47718         }
47719         this.result = this.handleResponse(response);
47720         return this.result;
47721     },
47722
47723     // utility functions used internally
47724     getUrl : function(appendParams){
47725         var url = this.options.url || this.form.url || this.form.el.dom.action;
47726         if(appendParams){
47727             var p = this.getParams();
47728             if(p){
47729                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
47730             }
47731         }
47732         return url;
47733     },
47734
47735     getMethod : function(){
47736         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
47737     },
47738
47739     getParams : function(){
47740         var bp = this.form.baseParams;
47741         var p = this.options.params;
47742         if(p){
47743             if(typeof p == "object"){
47744                 p = Roo.urlEncode(Roo.applyIf(p, bp));
47745             }else if(typeof p == 'string' && bp){
47746                 p += '&' + Roo.urlEncode(bp);
47747             }
47748         }else if(bp){
47749             p = Roo.urlEncode(bp);
47750         }
47751         return p;
47752     },
47753
47754     createCallback : function(){
47755         return {
47756             success: this.success,
47757             failure: this.failure,
47758             scope: this,
47759             timeout: (this.form.timeout*1000),
47760             upload: this.form.fileUpload ? this.success : undefined
47761         };
47762     }
47763 };
47764
47765 Roo.form.Action.Submit = function(form, options){
47766     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
47767 };
47768
47769 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
47770     type : 'submit',
47771
47772     haveProgress : false,
47773     uploadComplete : false,
47774     
47775     // uploadProgress indicator.
47776     uploadProgress : function()
47777     {
47778         if (!this.form.progressUrl) {
47779             return;
47780         }
47781         
47782         if (!this.haveProgress) {
47783             Roo.MessageBox.progress("Uploading", "Uploading");
47784         }
47785         if (this.uploadComplete) {
47786            Roo.MessageBox.hide();
47787            return;
47788         }
47789         
47790         this.haveProgress = true;
47791    
47792         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
47793         
47794         var c = new Roo.data.Connection();
47795         c.request({
47796             url : this.form.progressUrl,
47797             params: {
47798                 id : uid
47799             },
47800             method: 'GET',
47801             success : function(req){
47802                //console.log(data);
47803                 var rdata = false;
47804                 var edata;
47805                 try  {
47806                    rdata = Roo.decode(req.responseText)
47807                 } catch (e) {
47808                     Roo.log("Invalid data from server..");
47809                     Roo.log(edata);
47810                     return;
47811                 }
47812                 if (!rdata || !rdata.success) {
47813                     Roo.log(rdata);
47814                     Roo.MessageBox.alert(Roo.encode(rdata));
47815                     return;
47816                 }
47817                 var data = rdata.data;
47818                 
47819                 if (this.uploadComplete) {
47820                    Roo.MessageBox.hide();
47821                    return;
47822                 }
47823                    
47824                 if (data){
47825                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
47826                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
47827                     );
47828                 }
47829                 this.uploadProgress.defer(2000,this);
47830             },
47831        
47832             failure: function(data) {
47833                 Roo.log('progress url failed ');
47834                 Roo.log(data);
47835             },
47836             scope : this
47837         });
47838            
47839     },
47840     
47841     
47842     run : function()
47843     {
47844         // run get Values on the form, so it syncs any secondary forms.
47845         this.form.getValues();
47846         
47847         var o = this.options;
47848         var method = this.getMethod();
47849         var isPost = method == 'POST';
47850         if(o.clientValidation === false || this.form.isValid()){
47851             
47852             if (this.form.progressUrl) {
47853                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
47854                     (new Date() * 1) + '' + Math.random());
47855                     
47856             } 
47857             
47858             
47859             Roo.Ajax.request(Roo.apply(this.createCallback(), {
47860                 form:this.form.el.dom,
47861                 url:this.getUrl(!isPost),
47862                 method: method,
47863                 params:isPost ? this.getParams() : null,
47864                 isUpload: this.form.fileUpload
47865             }));
47866             
47867             this.uploadProgress();
47868
47869         }else if (o.clientValidation !== false){ // client validation failed
47870             this.failureType = Roo.form.Action.CLIENT_INVALID;
47871             this.form.afterAction(this, false);
47872         }
47873     },
47874
47875     success : function(response)
47876     {
47877         this.uploadComplete= true;
47878         if (this.haveProgress) {
47879             Roo.MessageBox.hide();
47880         }
47881         
47882         
47883         var result = this.processResponse(response);
47884         if(result === true || result.success){
47885             this.form.afterAction(this, true);
47886             return;
47887         }
47888         if(result.errors){
47889             this.form.markInvalid(result.errors);
47890             this.failureType = Roo.form.Action.SERVER_INVALID;
47891         }
47892         this.form.afterAction(this, false);
47893     },
47894     failure : function(response)
47895     {
47896         this.uploadComplete= true;
47897         if (this.haveProgress) {
47898             Roo.MessageBox.hide();
47899         }
47900         
47901         this.response = response;
47902         this.failureType = Roo.form.Action.CONNECT_FAILURE;
47903         this.form.afterAction(this, false);
47904     },
47905     
47906     handleResponse : function(response){
47907         if(this.form.errorReader){
47908             var rs = this.form.errorReader.read(response);
47909             var errors = [];
47910             if(rs.records){
47911                 for(var i = 0, len = rs.records.length; i < len; i++) {
47912                     var r = rs.records[i];
47913                     errors[i] = r.data;
47914                 }
47915             }
47916             if(errors.length < 1){
47917                 errors = null;
47918             }
47919             return {
47920                 success : rs.success,
47921                 errors : errors
47922             };
47923         }
47924         var ret = false;
47925         try {
47926             ret = Roo.decode(response.responseText);
47927         } catch (e) {
47928             ret = {
47929                 success: false,
47930                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
47931                 errors : []
47932             };
47933         }
47934         return ret;
47935         
47936     }
47937 });
47938
47939
47940 Roo.form.Action.Load = function(form, options){
47941     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
47942     this.reader = this.form.reader;
47943 };
47944
47945 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
47946     type : 'load',
47947
47948     run : function(){
47949         
47950         Roo.Ajax.request(Roo.apply(
47951                 this.createCallback(), {
47952                     method:this.getMethod(),
47953                     url:this.getUrl(false),
47954                     params:this.getParams()
47955         }));
47956     },
47957
47958     success : function(response){
47959         
47960         var result = this.processResponse(response);
47961         if(result === true || !result.success || !result.data){
47962             this.failureType = Roo.form.Action.LOAD_FAILURE;
47963             this.form.afterAction(this, false);
47964             return;
47965         }
47966         this.form.clearInvalid();
47967         this.form.setValues(result.data);
47968         this.form.afterAction(this, true);
47969     },
47970
47971     handleResponse : function(response){
47972         if(this.form.reader){
47973             var rs = this.form.reader.read(response);
47974             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
47975             return {
47976                 success : rs.success,
47977                 data : data
47978             };
47979         }
47980         return Roo.decode(response.responseText);
47981     }
47982 });
47983
47984 Roo.form.Action.ACTION_TYPES = {
47985     'load' : Roo.form.Action.Load,
47986     'submit' : Roo.form.Action.Submit
47987 };/*
47988  * Based on:
47989  * Ext JS Library 1.1.1
47990  * Copyright(c) 2006-2007, Ext JS, LLC.
47991  *
47992  * Originally Released Under LGPL - original licence link has changed is not relivant.
47993  *
47994  * Fork - LGPL
47995  * <script type="text/javascript">
47996  */
47997  
47998 /**
47999  * @class Roo.form.Layout
48000  * @extends Roo.Component
48001  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
48002  * @constructor
48003  * @param {Object} config Configuration options
48004  */
48005 Roo.form.Layout = function(config){
48006     var xitems = [];
48007     if (config.items) {
48008         xitems = config.items;
48009         delete config.items;
48010     }
48011     Roo.form.Layout.superclass.constructor.call(this, config);
48012     this.stack = [];
48013     Roo.each(xitems, this.addxtype, this);
48014      
48015 };
48016
48017 Roo.extend(Roo.form.Layout, Roo.Component, {
48018     /**
48019      * @cfg {String/Object} autoCreate
48020      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48021      */
48022     /**
48023      * @cfg {String/Object/Function} style
48024      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48025      * a function which returns such a specification.
48026      */
48027     /**
48028      * @cfg {String} labelAlign
48029      * Valid values are "left," "top" and "right" (defaults to "left")
48030      */
48031     /**
48032      * @cfg {Number} labelWidth
48033      * Fixed width in pixels of all field labels (defaults to undefined)
48034      */
48035     /**
48036      * @cfg {Boolean} clear
48037      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48038      */
48039     clear : true,
48040     /**
48041      * @cfg {String} labelSeparator
48042      * The separator to use after field labels (defaults to ':')
48043      */
48044     labelSeparator : ':',
48045     /**
48046      * @cfg {Boolean} hideLabels
48047      * True to suppress the display of field labels in this layout (defaults to false)
48048      */
48049     hideLabels : false,
48050
48051     // private
48052     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48053     
48054     isLayout : true,
48055     
48056     // private
48057     onRender : function(ct, position){
48058         if(this.el){ // from markup
48059             this.el = Roo.get(this.el);
48060         }else {  // generate
48061             var cfg = this.getAutoCreate();
48062             this.el = ct.createChild(cfg, position);
48063         }
48064         if(this.style){
48065             this.el.applyStyles(this.style);
48066         }
48067         if(this.labelAlign){
48068             this.el.addClass('x-form-label-'+this.labelAlign);
48069         }
48070         if(this.hideLabels){
48071             this.labelStyle = "display:none";
48072             this.elementStyle = "padding-left:0;";
48073         }else{
48074             if(typeof this.labelWidth == 'number'){
48075                 this.labelStyle = "width:"+this.labelWidth+"px;";
48076                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48077             }
48078             if(this.labelAlign == 'top'){
48079                 this.labelStyle = "width:auto;";
48080                 this.elementStyle = "padding-left:0;";
48081             }
48082         }
48083         var stack = this.stack;
48084         var slen = stack.length;
48085         if(slen > 0){
48086             if(!this.fieldTpl){
48087                 var t = new Roo.Template(
48088                     '<div class="x-form-item {5}">',
48089                         '<label for="{0}" style="{2}">{1}{4}</label>',
48090                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48091                         '</div>',
48092                     '</div><div class="x-form-clear-left"></div>'
48093                 );
48094                 t.disableFormats = true;
48095                 t.compile();
48096                 Roo.form.Layout.prototype.fieldTpl = t;
48097             }
48098             for(var i = 0; i < slen; i++) {
48099                 if(stack[i].isFormField){
48100                     this.renderField(stack[i]);
48101                 }else{
48102                     this.renderComponent(stack[i]);
48103                 }
48104             }
48105         }
48106         if(this.clear){
48107             this.el.createChild({cls:'x-form-clear'});
48108         }
48109     },
48110
48111     // private
48112     renderField : function(f){
48113         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48114                f.id, //0
48115                f.fieldLabel, //1
48116                f.labelStyle||this.labelStyle||'', //2
48117                this.elementStyle||'', //3
48118                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48119                f.itemCls||this.itemCls||''  //5
48120        ], true).getPrevSibling());
48121     },
48122
48123     // private
48124     renderComponent : function(c){
48125         c.render(c.isLayout ? this.el : this.el.createChild());    
48126     },
48127     /**
48128      * Adds a object form elements (using the xtype property as the factory method.)
48129      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48130      * @param {Object} config 
48131      */
48132     addxtype : function(o)
48133     {
48134         // create the lement.
48135         o.form = this.form;
48136         var fe = Roo.factory(o, Roo.form);
48137         this.form.allItems.push(fe);
48138         this.stack.push(fe);
48139         
48140         if (fe.isFormField) {
48141             this.form.items.add(fe);
48142         }
48143          
48144         return fe;
48145     }
48146 });
48147
48148 /**
48149  * @class Roo.form.Column
48150  * @extends Roo.form.Layout
48151  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
48152  * @constructor
48153  * @param {Object} config Configuration options
48154  */
48155 Roo.form.Column = function(config){
48156     Roo.form.Column.superclass.constructor.call(this, config);
48157 };
48158
48159 Roo.extend(Roo.form.Column, Roo.form.Layout, {
48160     /**
48161      * @cfg {Number/String} width
48162      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48163      */
48164     /**
48165      * @cfg {String/Object} autoCreate
48166      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
48167      */
48168
48169     // private
48170     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
48171
48172     // private
48173     onRender : function(ct, position){
48174         Roo.form.Column.superclass.onRender.call(this, ct, position);
48175         if(this.width){
48176             this.el.setWidth(this.width);
48177         }
48178     }
48179 });
48180
48181
48182 /**
48183  * @class Roo.form.Row
48184  * @extends Roo.form.Layout
48185  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
48186  * @constructor
48187  * @param {Object} config Configuration options
48188  */
48189
48190  
48191 Roo.form.Row = function(config){
48192     Roo.form.Row.superclass.constructor.call(this, config);
48193 };
48194  
48195 Roo.extend(Roo.form.Row, Roo.form.Layout, {
48196       /**
48197      * @cfg {Number/String} width
48198      * The fixed width of the column in pixels or CSS value (defaults to "auto")
48199      */
48200     /**
48201      * @cfg {Number/String} height
48202      * The fixed height of the column in pixels or CSS value (defaults to "auto")
48203      */
48204     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
48205     
48206     padWidth : 20,
48207     // private
48208     onRender : function(ct, position){
48209         //console.log('row render');
48210         if(!this.rowTpl){
48211             var t = new Roo.Template(
48212                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
48213                     '<label for="{0}" style="{2}">{1}{4}</label>',
48214                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48215                     '</div>',
48216                 '</div>'
48217             );
48218             t.disableFormats = true;
48219             t.compile();
48220             Roo.form.Layout.prototype.rowTpl = t;
48221         }
48222         this.fieldTpl = this.rowTpl;
48223         
48224         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
48225         var labelWidth = 100;
48226         
48227         if ((this.labelAlign != 'top')) {
48228             if (typeof this.labelWidth == 'number') {
48229                 labelWidth = this.labelWidth
48230             }
48231             this.padWidth =  20 + labelWidth;
48232             
48233         }
48234         
48235         Roo.form.Column.superclass.onRender.call(this, ct, position);
48236         if(this.width){
48237             this.el.setWidth(this.width);
48238         }
48239         if(this.height){
48240             this.el.setHeight(this.height);
48241         }
48242     },
48243     
48244     // private
48245     renderField : function(f){
48246         f.fieldEl = this.fieldTpl.append(this.el, [
48247                f.id, f.fieldLabel,
48248                f.labelStyle||this.labelStyle||'',
48249                this.elementStyle||'',
48250                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
48251                f.itemCls||this.itemCls||'',
48252                f.width ? f.width + this.padWidth : 160 + this.padWidth
48253        ],true);
48254     }
48255 });
48256  
48257
48258 /**
48259  * @class Roo.form.FieldSet
48260  * @extends Roo.form.Layout
48261  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
48262  * @constructor
48263  * @param {Object} config Configuration options
48264  */
48265 Roo.form.FieldSet = function(config){
48266     Roo.form.FieldSet.superclass.constructor.call(this, config);
48267 };
48268
48269 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
48270     /**
48271      * @cfg {String} legend
48272      * The text to display as the legend for the FieldSet (defaults to '')
48273      */
48274     /**
48275      * @cfg {String/Object} autoCreate
48276      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
48277      */
48278
48279     // private
48280     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
48281
48282     // private
48283     onRender : function(ct, position){
48284         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
48285         if(this.legend){
48286             this.setLegend(this.legend);
48287         }
48288     },
48289
48290     // private
48291     setLegend : function(text){
48292         if(this.rendered){
48293             this.el.child('legend').update(text);
48294         }
48295     }
48296 });/*
48297  * Based on:
48298  * Ext JS Library 1.1.1
48299  * Copyright(c) 2006-2007, Ext JS, LLC.
48300  *
48301  * Originally Released Under LGPL - original licence link has changed is not relivant.
48302  *
48303  * Fork - LGPL
48304  * <script type="text/javascript">
48305  */
48306 /**
48307  * @class Roo.form.VTypes
48308  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
48309  * @singleton
48310  */
48311 Roo.form.VTypes = function(){
48312     // closure these in so they are only created once.
48313     var alpha = /^[a-zA-Z_]+$/;
48314     var alphanum = /^[a-zA-Z0-9_]+$/;
48315     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
48316     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
48317
48318     // All these messages and functions are configurable
48319     return {
48320         /**
48321          * The function used to validate email addresses
48322          * @param {String} value The email address
48323          */
48324         'email' : function(v){
48325             return email.test(v);
48326         },
48327         /**
48328          * The error text to display when the email validation function returns false
48329          * @type String
48330          */
48331         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
48332         /**
48333          * The keystroke filter mask to be applied on email input
48334          * @type RegExp
48335          */
48336         'emailMask' : /[a-z0-9_\.\-@]/i,
48337
48338         /**
48339          * The function used to validate URLs
48340          * @param {String} value The URL
48341          */
48342         'url' : function(v){
48343             return url.test(v);
48344         },
48345         /**
48346          * The error text to display when the url validation function returns false
48347          * @type String
48348          */
48349         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
48350         
48351         /**
48352          * The function used to validate alpha values
48353          * @param {String} value The value
48354          */
48355         'alpha' : function(v){
48356             return alpha.test(v);
48357         },
48358         /**
48359          * The error text to display when the alpha validation function returns false
48360          * @type String
48361          */
48362         'alphaText' : 'This field should only contain letters and _',
48363         /**
48364          * The keystroke filter mask to be applied on alpha input
48365          * @type RegExp
48366          */
48367         'alphaMask' : /[a-z_]/i,
48368
48369         /**
48370          * The function used to validate alphanumeric values
48371          * @param {String} value The value
48372          */
48373         'alphanum' : function(v){
48374             return alphanum.test(v);
48375         },
48376         /**
48377          * The error text to display when the alphanumeric validation function returns false
48378          * @type String
48379          */
48380         'alphanumText' : 'This field should only contain letters, numbers and _',
48381         /**
48382          * The keystroke filter mask to be applied on alphanumeric input
48383          * @type RegExp
48384          */
48385         'alphanumMask' : /[a-z0-9_]/i
48386     };
48387 }();//<script type="text/javascript">
48388
48389 /**
48390  * @class Roo.form.FCKeditor
48391  * @extends Roo.form.TextArea
48392  * Wrapper around the FCKEditor http://www.fckeditor.net
48393  * @constructor
48394  * Creates a new FCKeditor
48395  * @param {Object} config Configuration options
48396  */
48397 Roo.form.FCKeditor = function(config){
48398     Roo.form.FCKeditor.superclass.constructor.call(this, config);
48399     this.addEvents({
48400          /**
48401          * @event editorinit
48402          * Fired when the editor is initialized - you can add extra handlers here..
48403          * @param {FCKeditor} this
48404          * @param {Object} the FCK object.
48405          */
48406         editorinit : true
48407     });
48408     
48409     
48410 };
48411 Roo.form.FCKeditor.editors = { };
48412 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
48413 {
48414     //defaultAutoCreate : {
48415     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
48416     //},
48417     // private
48418     /**
48419      * @cfg {Object} fck options - see fck manual for details.
48420      */
48421     fckconfig : false,
48422     
48423     /**
48424      * @cfg {Object} fck toolbar set (Basic or Default)
48425      */
48426     toolbarSet : 'Basic',
48427     /**
48428      * @cfg {Object} fck BasePath
48429      */ 
48430     basePath : '/fckeditor/',
48431     
48432     
48433     frame : false,
48434     
48435     value : '',
48436     
48437    
48438     onRender : function(ct, position)
48439     {
48440         if(!this.el){
48441             this.defaultAutoCreate = {
48442                 tag: "textarea",
48443                 style:"width:300px;height:60px;",
48444                 autocomplete: "new-password"
48445             };
48446         }
48447         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
48448         /*
48449         if(this.grow){
48450             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
48451             if(this.preventScrollbars){
48452                 this.el.setStyle("overflow", "hidden");
48453             }
48454             this.el.setHeight(this.growMin);
48455         }
48456         */
48457         //console.log('onrender' + this.getId() );
48458         Roo.form.FCKeditor.editors[this.getId()] = this;
48459          
48460
48461         this.replaceTextarea() ;
48462         
48463     },
48464     
48465     getEditor : function() {
48466         return this.fckEditor;
48467     },
48468     /**
48469      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
48470      * @param {Mixed} value The value to set
48471      */
48472     
48473     
48474     setValue : function(value)
48475     {
48476         //console.log('setValue: ' + value);
48477         
48478         if(typeof(value) == 'undefined') { // not sure why this is happending...
48479             return;
48480         }
48481         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48482         
48483         //if(!this.el || !this.getEditor()) {
48484         //    this.value = value;
48485             //this.setValue.defer(100,this,[value]);    
48486         //    return;
48487         //} 
48488         
48489         if(!this.getEditor()) {
48490             return;
48491         }
48492         
48493         this.getEditor().SetData(value);
48494         
48495         //
48496
48497     },
48498
48499     /**
48500      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
48501      * @return {Mixed} value The field value
48502      */
48503     getValue : function()
48504     {
48505         
48506         if (this.frame && this.frame.dom.style.display == 'none') {
48507             return Roo.form.FCKeditor.superclass.getValue.call(this);
48508         }
48509         
48510         if(!this.el || !this.getEditor()) {
48511            
48512            // this.getValue.defer(100,this); 
48513             return this.value;
48514         }
48515        
48516         
48517         var value=this.getEditor().GetData();
48518         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
48519         return Roo.form.FCKeditor.superclass.getValue.call(this);
48520         
48521
48522     },
48523
48524     /**
48525      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
48526      * @return {Mixed} value The field value
48527      */
48528     getRawValue : function()
48529     {
48530         if (this.frame && this.frame.dom.style.display == 'none') {
48531             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48532         }
48533         
48534         if(!this.el || !this.getEditor()) {
48535             //this.getRawValue.defer(100,this); 
48536             return this.value;
48537             return;
48538         }
48539         
48540         
48541         
48542         var value=this.getEditor().GetData();
48543         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
48544         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
48545          
48546     },
48547     
48548     setSize : function(w,h) {
48549         
48550         
48551         
48552         //if (this.frame && this.frame.dom.style.display == 'none') {
48553         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48554         //    return;
48555         //}
48556         //if(!this.el || !this.getEditor()) {
48557         //    this.setSize.defer(100,this, [w,h]); 
48558         //    return;
48559         //}
48560         
48561         
48562         
48563         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
48564         
48565         this.frame.dom.setAttribute('width', w);
48566         this.frame.dom.setAttribute('height', h);
48567         this.frame.setSize(w,h);
48568         
48569     },
48570     
48571     toggleSourceEdit : function(value) {
48572         
48573       
48574          
48575         this.el.dom.style.display = value ? '' : 'none';
48576         this.frame.dom.style.display = value ?  'none' : '';
48577         
48578     },
48579     
48580     
48581     focus: function(tag)
48582     {
48583         if (this.frame.dom.style.display == 'none') {
48584             return Roo.form.FCKeditor.superclass.focus.call(this);
48585         }
48586         if(!this.el || !this.getEditor()) {
48587             this.focus.defer(100,this, [tag]); 
48588             return;
48589         }
48590         
48591         
48592         
48593         
48594         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
48595         this.getEditor().Focus();
48596         if (tgs.length) {
48597             if (!this.getEditor().Selection.GetSelection()) {
48598                 this.focus.defer(100,this, [tag]); 
48599                 return;
48600             }
48601             
48602             
48603             var r = this.getEditor().EditorDocument.createRange();
48604             r.setStart(tgs[0],0);
48605             r.setEnd(tgs[0],0);
48606             this.getEditor().Selection.GetSelection().removeAllRanges();
48607             this.getEditor().Selection.GetSelection().addRange(r);
48608             this.getEditor().Focus();
48609         }
48610         
48611     },
48612     
48613     
48614     
48615     replaceTextarea : function()
48616     {
48617         if ( document.getElementById( this.getId() + '___Frame' ) ) {
48618             return ;
48619         }
48620         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
48621         //{
48622             // We must check the elements firstly using the Id and then the name.
48623         var oTextarea = document.getElementById( this.getId() );
48624         
48625         var colElementsByName = document.getElementsByName( this.getId() ) ;
48626          
48627         oTextarea.style.display = 'none' ;
48628
48629         if ( oTextarea.tabIndex ) {            
48630             this.TabIndex = oTextarea.tabIndex ;
48631         }
48632         
48633         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
48634         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
48635         this.frame = Roo.get(this.getId() + '___Frame')
48636     },
48637     
48638     _getConfigHtml : function()
48639     {
48640         var sConfig = '' ;
48641
48642         for ( var o in this.fckconfig ) {
48643             sConfig += sConfig.length > 0  ? '&amp;' : '';
48644             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
48645         }
48646
48647         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
48648     },
48649     
48650     
48651     _getIFrameHtml : function()
48652     {
48653         var sFile = 'fckeditor.html' ;
48654         /* no idea what this is about..
48655         try
48656         {
48657             if ( (/fcksource=true/i).test( window.top.location.search ) )
48658                 sFile = 'fckeditor.original.html' ;
48659         }
48660         catch (e) { 
48661         */
48662
48663         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
48664         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
48665         
48666         
48667         var html = '<iframe id="' + this.getId() +
48668             '___Frame" src="' + sLink +
48669             '" width="' + this.width +
48670             '" height="' + this.height + '"' +
48671             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
48672             ' frameborder="0" scrolling="no"></iframe>' ;
48673
48674         return html ;
48675     },
48676     
48677     _insertHtmlBefore : function( html, element )
48678     {
48679         if ( element.insertAdjacentHTML )       {
48680             // IE
48681             element.insertAdjacentHTML( 'beforeBegin', html ) ;
48682         } else { // Gecko
48683             var oRange = document.createRange() ;
48684             oRange.setStartBefore( element ) ;
48685             var oFragment = oRange.createContextualFragment( html );
48686             element.parentNode.insertBefore( oFragment, element ) ;
48687         }
48688     }
48689     
48690     
48691   
48692     
48693     
48694     
48695     
48696
48697 });
48698
48699 //Roo.reg('fckeditor', Roo.form.FCKeditor);
48700
48701 function FCKeditor_OnComplete(editorInstance){
48702     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
48703     f.fckEditor = editorInstance;
48704     //console.log("loaded");
48705     f.fireEvent('editorinit', f, editorInstance);
48706
48707   
48708
48709  
48710
48711
48712
48713
48714
48715
48716
48717
48718
48719
48720
48721
48722
48723
48724
48725 //<script type="text/javascript">
48726 /**
48727  * @class Roo.form.GridField
48728  * @extends Roo.form.Field
48729  * Embed a grid (or editable grid into a form)
48730  * STATUS ALPHA
48731  * 
48732  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
48733  * it needs 
48734  * xgrid.store = Roo.data.Store
48735  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
48736  * xgrid.store.reader = Roo.data.JsonReader 
48737  * 
48738  * 
48739  * @constructor
48740  * Creates a new GridField
48741  * @param {Object} config Configuration options
48742  */
48743 Roo.form.GridField = function(config){
48744     Roo.form.GridField.superclass.constructor.call(this, config);
48745      
48746 };
48747
48748 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
48749     /**
48750      * @cfg {Number} width  - used to restrict width of grid..
48751      */
48752     width : 100,
48753     /**
48754      * @cfg {Number} height - used to restrict height of grid..
48755      */
48756     height : 50,
48757      /**
48758      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
48759          * 
48760          *}
48761      */
48762     xgrid : false, 
48763     /**
48764      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48765      * {tag: "input", type: "checkbox", autocomplete: "off"})
48766      */
48767    // defaultAutoCreate : { tag: 'div' },
48768     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
48769     /**
48770      * @cfg {String} addTitle Text to include for adding a title.
48771      */
48772     addTitle : false,
48773     //
48774     onResize : function(){
48775         Roo.form.Field.superclass.onResize.apply(this, arguments);
48776     },
48777
48778     initEvents : function(){
48779         // Roo.form.Checkbox.superclass.initEvents.call(this);
48780         // has no events...
48781        
48782     },
48783
48784
48785     getResizeEl : function(){
48786         return this.wrap;
48787     },
48788
48789     getPositionEl : function(){
48790         return this.wrap;
48791     },
48792
48793     // private
48794     onRender : function(ct, position){
48795         
48796         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
48797         var style = this.style;
48798         delete this.style;
48799         
48800         Roo.form.GridField.superclass.onRender.call(this, ct, position);
48801         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
48802         this.viewEl = this.wrap.createChild({ tag: 'div' });
48803         if (style) {
48804             this.viewEl.applyStyles(style);
48805         }
48806         if (this.width) {
48807             this.viewEl.setWidth(this.width);
48808         }
48809         if (this.height) {
48810             this.viewEl.setHeight(this.height);
48811         }
48812         //if(this.inputValue !== undefined){
48813         //this.setValue(this.value);
48814         
48815         
48816         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
48817         
48818         
48819         this.grid.render();
48820         this.grid.getDataSource().on('remove', this.refreshValue, this);
48821         this.grid.getDataSource().on('update', this.refreshValue, this);
48822         this.grid.on('afteredit', this.refreshValue, this);
48823  
48824     },
48825      
48826     
48827     /**
48828      * Sets the value of the item. 
48829      * @param {String} either an object  or a string..
48830      */
48831     setValue : function(v){
48832         //this.value = v;
48833         v = v || []; // empty set..
48834         // this does not seem smart - it really only affects memoryproxy grids..
48835         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
48836             var ds = this.grid.getDataSource();
48837             // assumes a json reader..
48838             var data = {}
48839             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
48840             ds.loadData( data);
48841         }
48842         // clear selection so it does not get stale.
48843         if (this.grid.sm) { 
48844             this.grid.sm.clearSelections();
48845         }
48846         
48847         Roo.form.GridField.superclass.setValue.call(this, v);
48848         this.refreshValue();
48849         // should load data in the grid really....
48850     },
48851     
48852     // private
48853     refreshValue: function() {
48854          var val = [];
48855         this.grid.getDataSource().each(function(r) {
48856             val.push(r.data);
48857         });
48858         this.el.dom.value = Roo.encode(val);
48859     }
48860     
48861      
48862     
48863     
48864 });/*
48865  * Based on:
48866  * Ext JS Library 1.1.1
48867  * Copyright(c) 2006-2007, Ext JS, LLC.
48868  *
48869  * Originally Released Under LGPL - original licence link has changed is not relivant.
48870  *
48871  * Fork - LGPL
48872  * <script type="text/javascript">
48873  */
48874 /**
48875  * @class Roo.form.DisplayField
48876  * @extends Roo.form.Field
48877  * A generic Field to display non-editable data.
48878  * @cfg {Boolean} closable (true|false) default false
48879  * @constructor
48880  * Creates a new Display Field item.
48881  * @param {Object} config Configuration options
48882  */
48883 Roo.form.DisplayField = function(config){
48884     Roo.form.DisplayField.superclass.constructor.call(this, config);
48885     
48886     this.addEvents({
48887         /**
48888          * @event close
48889          * Fires after the click the close btn
48890              * @param {Roo.form.DisplayField} this
48891              */
48892         close : true
48893     });
48894 };
48895
48896 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
48897     inputType:      'hidden',
48898     allowBlank:     true,
48899     readOnly:         true,
48900     
48901  
48902     /**
48903      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
48904      */
48905     focusClass : undefined,
48906     /**
48907      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
48908      */
48909     fieldClass: 'x-form-field',
48910     
48911      /**
48912      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
48913      */
48914     valueRenderer: undefined,
48915     
48916     width: 100,
48917     /**
48918      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
48919      * {tag: "input", type: "checkbox", autocomplete: "off"})
48920      */
48921      
48922  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
48923  
48924     closable : false,
48925     
48926     onResize : function(){
48927         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
48928         
48929     },
48930
48931     initEvents : function(){
48932         // Roo.form.Checkbox.superclass.initEvents.call(this);
48933         // has no events...
48934         
48935         if(this.closable){
48936             this.closeEl.on('click', this.onClose, this);
48937         }
48938        
48939     },
48940
48941
48942     getResizeEl : function(){
48943         return this.wrap;
48944     },
48945
48946     getPositionEl : function(){
48947         return this.wrap;
48948     },
48949
48950     // private
48951     onRender : function(ct, position){
48952         
48953         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
48954         //if(this.inputValue !== undefined){
48955         this.wrap = this.el.wrap();
48956         
48957         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
48958         
48959         if(this.closable){
48960             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
48961         }
48962         
48963         if (this.bodyStyle) {
48964             this.viewEl.applyStyles(this.bodyStyle);
48965         }
48966         //this.viewEl.setStyle('padding', '2px');
48967         
48968         this.setValue(this.value);
48969         
48970     },
48971 /*
48972     // private
48973     initValue : Roo.emptyFn,
48974
48975   */
48976
48977         // private
48978     onClick : function(){
48979         
48980     },
48981
48982     /**
48983      * Sets the checked state of the checkbox.
48984      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
48985      */
48986     setValue : function(v){
48987         this.value = v;
48988         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
48989         // this might be called before we have a dom element..
48990         if (!this.viewEl) {
48991             return;
48992         }
48993         this.viewEl.dom.innerHTML = html;
48994         Roo.form.DisplayField.superclass.setValue.call(this, v);
48995
48996     },
48997     
48998     onClose : function(e)
48999     {
49000         e.preventDefault();
49001         
49002         this.fireEvent('close', this);
49003     }
49004 });/*
49005  * 
49006  * Licence- LGPL
49007  * 
49008  */
49009
49010 /**
49011  * @class Roo.form.DayPicker
49012  * @extends Roo.form.Field
49013  * A Day picker show [M] [T] [W] ....
49014  * @constructor
49015  * Creates a new Day Picker
49016  * @param {Object} config Configuration options
49017  */
49018 Roo.form.DayPicker= function(config){
49019     Roo.form.DayPicker.superclass.constructor.call(this, config);
49020      
49021 };
49022
49023 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49024     /**
49025      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49026      */
49027     focusClass : undefined,
49028     /**
49029      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49030      */
49031     fieldClass: "x-form-field",
49032    
49033     /**
49034      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49035      * {tag: "input", type: "checkbox", autocomplete: "off"})
49036      */
49037     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49038     
49039    
49040     actionMode : 'viewEl', 
49041     //
49042     // private
49043  
49044     inputType : 'hidden',
49045     
49046      
49047     inputElement: false, // real input element?
49048     basedOn: false, // ????
49049     
49050     isFormField: true, // not sure where this is needed!!!!
49051
49052     onResize : function(){
49053         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49054         if(!this.boxLabel){
49055             this.el.alignTo(this.wrap, 'c-c');
49056         }
49057     },
49058
49059     initEvents : function(){
49060         Roo.form.Checkbox.superclass.initEvents.call(this);
49061         this.el.on("click", this.onClick,  this);
49062         this.el.on("change", this.onClick,  this);
49063     },
49064
49065
49066     getResizeEl : function(){
49067         return this.wrap;
49068     },
49069
49070     getPositionEl : function(){
49071         return this.wrap;
49072     },
49073
49074     
49075     // private
49076     onRender : function(ct, position){
49077         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49078        
49079         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49080         
49081         var r1 = '<table><tr>';
49082         var r2 = '<tr class="x-form-daypick-icons">';
49083         for (var i=0; i < 7; i++) {
49084             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49085             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49086         }
49087         
49088         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49089         viewEl.select('img').on('click', this.onClick, this);
49090         this.viewEl = viewEl;   
49091         
49092         
49093         // this will not work on Chrome!!!
49094         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49095         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49096         
49097         
49098           
49099
49100     },
49101
49102     // private
49103     initValue : Roo.emptyFn,
49104
49105     /**
49106      * Returns the checked state of the checkbox.
49107      * @return {Boolean} True if checked, else false
49108      */
49109     getValue : function(){
49110         return this.el.dom.value;
49111         
49112     },
49113
49114         // private
49115     onClick : function(e){ 
49116         //this.setChecked(!this.checked);
49117         Roo.get(e.target).toggleClass('x-menu-item-checked');
49118         this.refreshValue();
49119         //if(this.el.dom.checked != this.checked){
49120         //    this.setValue(this.el.dom.checked);
49121        // }
49122     },
49123     
49124     // private
49125     refreshValue : function()
49126     {
49127         var val = '';
49128         this.viewEl.select('img',true).each(function(e,i,n)  {
49129             val += e.is(".x-menu-item-checked") ? String(n) : '';
49130         });
49131         this.setValue(val, true);
49132     },
49133
49134     /**
49135      * Sets the checked state of the checkbox.
49136      * On is always based on a string comparison between inputValue and the param.
49137      * @param {Boolean/String} value - the value to set 
49138      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49139      */
49140     setValue : function(v,suppressEvent){
49141         if (!this.el.dom) {
49142             return;
49143         }
49144         var old = this.el.dom.value ;
49145         this.el.dom.value = v;
49146         if (suppressEvent) {
49147             return ;
49148         }
49149          
49150         // update display..
49151         this.viewEl.select('img',true).each(function(e,i,n)  {
49152             
49153             var on = e.is(".x-menu-item-checked");
49154             var newv = v.indexOf(String(n)) > -1;
49155             if (on != newv) {
49156                 e.toggleClass('x-menu-item-checked');
49157             }
49158             
49159         });
49160         
49161         
49162         this.fireEvent('change', this, v, old);
49163         
49164         
49165     },
49166    
49167     // handle setting of hidden value by some other method!!?!?
49168     setFromHidden: function()
49169     {
49170         if(!this.el){
49171             return;
49172         }
49173         //console.log("SET FROM HIDDEN");
49174         //alert('setFrom hidden');
49175         this.setValue(this.el.dom.value);
49176     },
49177     
49178     onDestroy : function()
49179     {
49180         if(this.viewEl){
49181             Roo.get(this.viewEl).remove();
49182         }
49183          
49184         Roo.form.DayPicker.superclass.onDestroy.call(this);
49185     }
49186
49187 });/*
49188  * RooJS Library 1.1.1
49189  * Copyright(c) 2008-2011  Alan Knowles
49190  *
49191  * License - LGPL
49192  */
49193  
49194
49195 /**
49196  * @class Roo.form.ComboCheck
49197  * @extends Roo.form.ComboBox
49198  * A combobox for multiple select items.
49199  *
49200  * FIXME - could do with a reset button..
49201  * 
49202  * @constructor
49203  * Create a new ComboCheck
49204  * @param {Object} config Configuration options
49205  */
49206 Roo.form.ComboCheck = function(config){
49207     Roo.form.ComboCheck.superclass.constructor.call(this, config);
49208     // should verify some data...
49209     // like
49210     // hiddenName = required..
49211     // displayField = required
49212     // valudField == required
49213     var req= [ 'hiddenName', 'displayField', 'valueField' ];
49214     var _t = this;
49215     Roo.each(req, function(e) {
49216         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
49217             throw "Roo.form.ComboCheck : missing value for: " + e;
49218         }
49219     });
49220     
49221     
49222 };
49223
49224 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
49225      
49226      
49227     editable : false,
49228      
49229     selectedClass: 'x-menu-item-checked', 
49230     
49231     // private
49232     onRender : function(ct, position){
49233         var _t = this;
49234         
49235         
49236         
49237         if(!this.tpl){
49238             var cls = 'x-combo-list';
49239
49240             
49241             this.tpl =  new Roo.Template({
49242                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
49243                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
49244                    '<span>{' + this.displayField + '}</span>' +
49245                     '</div>' 
49246                 
49247             });
49248         }
49249  
49250         
49251         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
49252         this.view.singleSelect = false;
49253         this.view.multiSelect = true;
49254         this.view.toggleSelect = true;
49255         this.pageTb.add(new Roo.Toolbar.Fill(), {
49256             
49257             text: 'Done',
49258             handler: function()
49259             {
49260                 _t.collapse();
49261             }
49262         });
49263     },
49264     
49265     onViewOver : function(e, t){
49266         // do nothing...
49267         return;
49268         
49269     },
49270     
49271     onViewClick : function(doFocus,index){
49272         return;
49273         
49274     },
49275     select: function () {
49276         //Roo.log("SELECT CALLED");
49277     },
49278      
49279     selectByValue : function(xv, scrollIntoView){
49280         var ar = this.getValueArray();
49281         var sels = [];
49282         
49283         Roo.each(ar, function(v) {
49284             if(v === undefined || v === null){
49285                 return;
49286             }
49287             var r = this.findRecord(this.valueField, v);
49288             if(r){
49289                 sels.push(this.store.indexOf(r))
49290                 
49291             }
49292         },this);
49293         this.view.select(sels);
49294         return false;
49295     },
49296     
49297     
49298     
49299     onSelect : function(record, index){
49300        // Roo.log("onselect Called");
49301        // this is only called by the clear button now..
49302         this.view.clearSelections();
49303         this.setValue('[]');
49304         if (this.value != this.valueBefore) {
49305             this.fireEvent('change', this, this.value, this.valueBefore);
49306             this.valueBefore = this.value;
49307         }
49308     },
49309     getValueArray : function()
49310     {
49311         var ar = [] ;
49312         
49313         try {
49314             //Roo.log(this.value);
49315             if (typeof(this.value) == 'undefined') {
49316                 return [];
49317             }
49318             var ar = Roo.decode(this.value);
49319             return  ar instanceof Array ? ar : []; //?? valid?
49320             
49321         } catch(e) {
49322             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
49323             return [];
49324         }
49325          
49326     },
49327     expand : function ()
49328     {
49329         
49330         Roo.form.ComboCheck.superclass.expand.call(this);
49331         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
49332         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
49333         
49334
49335     },
49336     
49337     collapse : function(){
49338         Roo.form.ComboCheck.superclass.collapse.call(this);
49339         var sl = this.view.getSelectedIndexes();
49340         var st = this.store;
49341         var nv = [];
49342         var tv = [];
49343         var r;
49344         Roo.each(sl, function(i) {
49345             r = st.getAt(i);
49346             nv.push(r.get(this.valueField));
49347         },this);
49348         this.setValue(Roo.encode(nv));
49349         if (this.value != this.valueBefore) {
49350
49351             this.fireEvent('change', this, this.value, this.valueBefore);
49352             this.valueBefore = this.value;
49353         }
49354         
49355     },
49356     
49357     setValue : function(v){
49358         // Roo.log(v);
49359         this.value = v;
49360         
49361         var vals = this.getValueArray();
49362         var tv = [];
49363         Roo.each(vals, function(k) {
49364             var r = this.findRecord(this.valueField, k);
49365             if(r){
49366                 tv.push(r.data[this.displayField]);
49367             }else if(this.valueNotFoundText !== undefined){
49368                 tv.push( this.valueNotFoundText );
49369             }
49370         },this);
49371        // Roo.log(tv);
49372         
49373         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
49374         this.hiddenField.value = v;
49375         this.value = v;
49376     }
49377     
49378 });/*
49379  * Based on:
49380  * Ext JS Library 1.1.1
49381  * Copyright(c) 2006-2007, Ext JS, LLC.
49382  *
49383  * Originally Released Under LGPL - original licence link has changed is not relivant.
49384  *
49385  * Fork - LGPL
49386  * <script type="text/javascript">
49387  */
49388  
49389 /**
49390  * @class Roo.form.Signature
49391  * @extends Roo.form.Field
49392  * Signature field.  
49393  * @constructor
49394  * 
49395  * @param {Object} config Configuration options
49396  */
49397
49398 Roo.form.Signature = function(config){
49399     Roo.form.Signature.superclass.constructor.call(this, config);
49400     
49401     this.addEvents({// not in used??
49402          /**
49403          * @event confirm
49404          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
49405              * @param {Roo.form.Signature} combo This combo box
49406              */
49407         'confirm' : true,
49408         /**
49409          * @event reset
49410          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
49411              * @param {Roo.form.ComboBox} combo This combo box
49412              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
49413              */
49414         'reset' : true
49415     });
49416 };
49417
49418 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
49419     /**
49420      * @cfg {Object} labels Label to use when rendering a form.
49421      * defaults to 
49422      * labels : { 
49423      *      clear : "Clear",
49424      *      confirm : "Confirm"
49425      *  }
49426      */
49427     labels : { 
49428         clear : "Clear",
49429         confirm : "Confirm"
49430     },
49431     /**
49432      * @cfg {Number} width The signature panel width (defaults to 300)
49433      */
49434     width: 300,
49435     /**
49436      * @cfg {Number} height The signature panel height (defaults to 100)
49437      */
49438     height : 100,
49439     /**
49440      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
49441      */
49442     allowBlank : false,
49443     
49444     //private
49445     // {Object} signPanel The signature SVG panel element (defaults to {})
49446     signPanel : {},
49447     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
49448     isMouseDown : false,
49449     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
49450     isConfirmed : false,
49451     // {String} signatureTmp SVG mapping string (defaults to empty string)
49452     signatureTmp : '',
49453     
49454     
49455     defaultAutoCreate : { // modified by initCompnoent..
49456         tag: "input",
49457         type:"hidden"
49458     },
49459
49460     // private
49461     onRender : function(ct, position){
49462         
49463         Roo.form.Signature.superclass.onRender.call(this, ct, position);
49464         
49465         this.wrap = this.el.wrap({
49466             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
49467         });
49468         
49469         this.createToolbar(this);
49470         this.signPanel = this.wrap.createChild({
49471                 tag: 'div',
49472                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
49473             }, this.el
49474         );
49475             
49476         this.svgID = Roo.id();
49477         this.svgEl = this.signPanel.createChild({
49478               xmlns : 'http://www.w3.org/2000/svg',
49479               tag : 'svg',
49480               id : this.svgID + "-svg",
49481               width: this.width,
49482               height: this.height,
49483               viewBox: '0 0 '+this.width+' '+this.height,
49484               cn : [
49485                 {
49486                     tag: "rect",
49487                     id: this.svgID + "-svg-r",
49488                     width: this.width,
49489                     height: this.height,
49490                     fill: "#ffa"
49491                 },
49492                 {
49493                     tag: "line",
49494                     id: this.svgID + "-svg-l",
49495                     x1: "0", // start
49496                     y1: (this.height*0.8), // start set the line in 80% of height
49497                     x2: this.width, // end
49498                     y2: (this.height*0.8), // end set the line in 80% of height
49499                     'stroke': "#666",
49500                     'stroke-width': "1",
49501                     'stroke-dasharray': "3",
49502                     'shape-rendering': "crispEdges",
49503                     'pointer-events': "none"
49504                 },
49505                 {
49506                     tag: "path",
49507                     id: this.svgID + "-svg-p",
49508                     'stroke': "navy",
49509                     'stroke-width': "3",
49510                     'fill': "none",
49511                     'pointer-events': 'none'
49512                 }
49513               ]
49514         });
49515         this.createSVG();
49516         this.svgBox = this.svgEl.dom.getScreenCTM();
49517     },
49518     createSVG : function(){ 
49519         var svg = this.signPanel;
49520         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
49521         var t = this;
49522
49523         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
49524         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
49525         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
49526         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
49527         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
49528         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
49529         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
49530         
49531     },
49532     isTouchEvent : function(e){
49533         return e.type.match(/^touch/);
49534     },
49535     getCoords : function (e) {
49536         var pt    = this.svgEl.dom.createSVGPoint();
49537         pt.x = e.clientX; 
49538         pt.y = e.clientY;
49539         if (this.isTouchEvent(e)) {
49540             pt.x =  e.targetTouches[0].clientX;
49541             pt.y = e.targetTouches[0].clientY;
49542         }
49543         var a = this.svgEl.dom.getScreenCTM();
49544         var b = a.inverse();
49545         var mx = pt.matrixTransform(b);
49546         return mx.x + ',' + mx.y;
49547     },
49548     //mouse event headler 
49549     down : function (e) {
49550         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
49551         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
49552         
49553         this.isMouseDown = true;
49554         
49555         e.preventDefault();
49556     },
49557     move : function (e) {
49558         if (this.isMouseDown) {
49559             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
49560             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
49561         }
49562         
49563         e.preventDefault();
49564     },
49565     up : function (e) {
49566         this.isMouseDown = false;
49567         var sp = this.signatureTmp.split(' ');
49568         
49569         if(sp.length > 1){
49570             if(!sp[sp.length-2].match(/^L/)){
49571                 sp.pop();
49572                 sp.pop();
49573                 sp.push("");
49574                 this.signatureTmp = sp.join(" ");
49575             }
49576         }
49577         if(this.getValue() != this.signatureTmp){
49578             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49579             this.isConfirmed = false;
49580         }
49581         e.preventDefault();
49582     },
49583     
49584     /**
49585      * Protected method that will not generally be called directly. It
49586      * is called when the editor creates its toolbar. Override this method if you need to
49587      * add custom toolbar buttons.
49588      * @param {HtmlEditor} editor
49589      */
49590     createToolbar : function(editor){
49591          function btn(id, toggle, handler){
49592             var xid = fid + '-'+ id ;
49593             return {
49594                 id : xid,
49595                 cmd : id,
49596                 cls : 'x-btn-icon x-edit-'+id,
49597                 enableToggle:toggle !== false,
49598                 scope: editor, // was editor...
49599                 handler:handler||editor.relayBtnCmd,
49600                 clickEvent:'mousedown',
49601                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49602                 tabIndex:-1
49603             };
49604         }
49605         
49606         
49607         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
49608         this.tb = tb;
49609         this.tb.add(
49610            {
49611                 cls : ' x-signature-btn x-signature-'+id,
49612                 scope: editor, // was editor...
49613                 handler: this.reset,
49614                 clickEvent:'mousedown',
49615                 text: this.labels.clear
49616             },
49617             {
49618                  xtype : 'Fill',
49619                  xns: Roo.Toolbar
49620             }, 
49621             {
49622                 cls : '  x-signature-btn x-signature-'+id,
49623                 scope: editor, // was editor...
49624                 handler: this.confirmHandler,
49625                 clickEvent:'mousedown',
49626                 text: this.labels.confirm
49627             }
49628         );
49629     
49630     },
49631     //public
49632     /**
49633      * when user is clicked confirm then show this image.....
49634      * 
49635      * @return {String} Image Data URI
49636      */
49637     getImageDataURI : function(){
49638         var svg = this.svgEl.dom.parentNode.innerHTML;
49639         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
49640         return src; 
49641     },
49642     /**
49643      * 
49644      * @return {Boolean} this.isConfirmed
49645      */
49646     getConfirmed : function(){
49647         return this.isConfirmed;
49648     },
49649     /**
49650      * 
49651      * @return {Number} this.width
49652      */
49653     getWidth : function(){
49654         return this.width;
49655     },
49656     /**
49657      * 
49658      * @return {Number} this.height
49659      */
49660     getHeight : function(){
49661         return this.height;
49662     },
49663     // private
49664     getSignature : function(){
49665         return this.signatureTmp;
49666     },
49667     // private
49668     reset : function(){
49669         this.signatureTmp = '';
49670         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49671         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
49672         this.isConfirmed = false;
49673         Roo.form.Signature.superclass.reset.call(this);
49674     },
49675     setSignature : function(s){
49676         this.signatureTmp = s;
49677         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
49678         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
49679         this.setValue(s);
49680         this.isConfirmed = false;
49681         Roo.form.Signature.superclass.reset.call(this);
49682     }, 
49683     test : function(){
49684 //        Roo.log(this.signPanel.dom.contentWindow.up())
49685     },
49686     //private
49687     setConfirmed : function(){
49688         
49689         
49690         
49691 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
49692     },
49693     // private
49694     confirmHandler : function(){
49695         if(!this.getSignature()){
49696             return;
49697         }
49698         
49699         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
49700         this.setValue(this.getSignature());
49701         this.isConfirmed = true;
49702         
49703         this.fireEvent('confirm', this);
49704     },
49705     // private
49706     // Subclasses should provide the validation implementation by overriding this
49707     validateValue : function(value){
49708         if(this.allowBlank){
49709             return true;
49710         }
49711         
49712         if(this.isConfirmed){
49713             return true;
49714         }
49715         return false;
49716     }
49717 });/*
49718  * Based on:
49719  * Ext JS Library 1.1.1
49720  * Copyright(c) 2006-2007, Ext JS, LLC.
49721  *
49722  * Originally Released Under LGPL - original licence link has changed is not relivant.
49723  *
49724  * Fork - LGPL
49725  * <script type="text/javascript">
49726  */
49727  
49728
49729 /**
49730  * @class Roo.form.ComboBox
49731  * @extends Roo.form.TriggerField
49732  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
49733  * @constructor
49734  * Create a new ComboBox.
49735  * @param {Object} config Configuration options
49736  */
49737 Roo.form.Select = function(config){
49738     Roo.form.Select.superclass.constructor.call(this, config);
49739      
49740 };
49741
49742 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
49743     /**
49744      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
49745      */
49746     /**
49747      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
49748      * rendering into an Roo.Editor, defaults to false)
49749      */
49750     /**
49751      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
49752      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
49753      */
49754     /**
49755      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
49756      */
49757     /**
49758      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
49759      * the dropdown list (defaults to undefined, with no header element)
49760      */
49761
49762      /**
49763      * @cfg {String/Roo.Template} tpl The template to use to render the output
49764      */
49765      
49766     // private
49767     defaultAutoCreate : {tag: "select"  },
49768     /**
49769      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
49770      */
49771     listWidth: undefined,
49772     /**
49773      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
49774      * mode = 'remote' or 'text' if mode = 'local')
49775      */
49776     displayField: undefined,
49777     /**
49778      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
49779      * mode = 'remote' or 'value' if mode = 'local'). 
49780      * Note: use of a valueField requires the user make a selection
49781      * in order for a value to be mapped.
49782      */
49783     valueField: undefined,
49784     
49785     
49786     /**
49787      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
49788      * field's data value (defaults to the underlying DOM element's name)
49789      */
49790     hiddenName: undefined,
49791     /**
49792      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
49793      */
49794     listClass: '',
49795     /**
49796      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
49797      */
49798     selectedClass: 'x-combo-selected',
49799     /**
49800      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
49801      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
49802      * which displays a downward arrow icon).
49803      */
49804     triggerClass : 'x-form-arrow-trigger',
49805     /**
49806      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
49807      */
49808     shadow:'sides',
49809     /**
49810      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
49811      * anchor positions (defaults to 'tl-bl')
49812      */
49813     listAlign: 'tl-bl?',
49814     /**
49815      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
49816      */
49817     maxHeight: 300,
49818     /**
49819      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
49820      * query specified by the allQuery config option (defaults to 'query')
49821      */
49822     triggerAction: 'query',
49823     /**
49824      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
49825      * (defaults to 4, does not apply if editable = false)
49826      */
49827     minChars : 4,
49828     /**
49829      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
49830      * delay (typeAheadDelay) if it matches a known value (defaults to false)
49831      */
49832     typeAhead: false,
49833     /**
49834      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
49835      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
49836      */
49837     queryDelay: 500,
49838     /**
49839      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
49840      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
49841      */
49842     pageSize: 0,
49843     /**
49844      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
49845      * when editable = true (defaults to false)
49846      */
49847     selectOnFocus:false,
49848     /**
49849      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
49850      */
49851     queryParam: 'query',
49852     /**
49853      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
49854      * when mode = 'remote' (defaults to 'Loading...')
49855      */
49856     loadingText: 'Loading...',
49857     /**
49858      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
49859      */
49860     resizable: false,
49861     /**
49862      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
49863      */
49864     handleHeight : 8,
49865     /**
49866      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
49867      * traditional select (defaults to true)
49868      */
49869     editable: true,
49870     /**
49871      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
49872      */
49873     allQuery: '',
49874     /**
49875      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
49876      */
49877     mode: 'remote',
49878     /**
49879      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
49880      * listWidth has a higher value)
49881      */
49882     minListWidth : 70,
49883     /**
49884      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
49885      * allow the user to set arbitrary text into the field (defaults to false)
49886      */
49887     forceSelection:false,
49888     /**
49889      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
49890      * if typeAhead = true (defaults to 250)
49891      */
49892     typeAheadDelay : 250,
49893     /**
49894      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
49895      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
49896      */
49897     valueNotFoundText : undefined,
49898     
49899     /**
49900      * @cfg {String} defaultValue The value displayed after loading the store.
49901      */
49902     defaultValue: '',
49903     
49904     /**
49905      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
49906      */
49907     blockFocus : false,
49908     
49909     /**
49910      * @cfg {Boolean} disableClear Disable showing of clear button.
49911      */
49912     disableClear : false,
49913     /**
49914      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
49915      */
49916     alwaysQuery : false,
49917     
49918     //private
49919     addicon : false,
49920     editicon: false,
49921     
49922     // element that contains real text value.. (when hidden is used..)
49923      
49924     // private
49925     onRender : function(ct, position){
49926         Roo.form.Field.prototype.onRender.call(this, ct, position);
49927         
49928         if(this.store){
49929             this.store.on('beforeload', this.onBeforeLoad, this);
49930             this.store.on('load', this.onLoad, this);
49931             this.store.on('loadexception', this.onLoadException, this);
49932             this.store.load({});
49933         }
49934         
49935         
49936         
49937     },
49938
49939     // private
49940     initEvents : function(){
49941         //Roo.form.ComboBox.superclass.initEvents.call(this);
49942  
49943     },
49944
49945     onDestroy : function(){
49946        
49947         if(this.store){
49948             this.store.un('beforeload', this.onBeforeLoad, this);
49949             this.store.un('load', this.onLoad, this);
49950             this.store.un('loadexception', this.onLoadException, this);
49951         }
49952         //Roo.form.ComboBox.superclass.onDestroy.call(this);
49953     },
49954
49955     // private
49956     fireKey : function(e){
49957         if(e.isNavKeyPress() && !this.list.isVisible()){
49958             this.fireEvent("specialkey", this, e);
49959         }
49960     },
49961
49962     // private
49963     onResize: function(w, h){
49964         
49965         return; 
49966     
49967         
49968     },
49969
49970     /**
49971      * Allow or prevent the user from directly editing the field text.  If false is passed,
49972      * the user will only be able to select from the items defined in the dropdown list.  This method
49973      * is the runtime equivalent of setting the 'editable' config option at config time.
49974      * @param {Boolean} value True to allow the user to directly edit the field text
49975      */
49976     setEditable : function(value){
49977          
49978     },
49979
49980     // private
49981     onBeforeLoad : function(){
49982         
49983         Roo.log("Select before load");
49984         return;
49985     
49986         this.innerList.update(this.loadingText ?
49987                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
49988         //this.restrictHeight();
49989         this.selectedIndex = -1;
49990     },
49991
49992     // private
49993     onLoad : function(){
49994
49995     
49996         var dom = this.el.dom;
49997         dom.innerHTML = '';
49998          var od = dom.ownerDocument;
49999          
50000         if (this.emptyText) {
50001             var op = od.createElement('option');
50002             op.setAttribute('value', '');
50003             op.innerHTML = String.format('{0}', this.emptyText);
50004             dom.appendChild(op);
50005         }
50006         if(this.store.getCount() > 0){
50007            
50008             var vf = this.valueField;
50009             var df = this.displayField;
50010             this.store.data.each(function(r) {
50011                 // which colmsn to use... testing - cdoe / title..
50012                 var op = od.createElement('option');
50013                 op.setAttribute('value', r.data[vf]);
50014                 op.innerHTML = String.format('{0}', r.data[df]);
50015                 dom.appendChild(op);
50016             });
50017             if (typeof(this.defaultValue != 'undefined')) {
50018                 this.setValue(this.defaultValue);
50019             }
50020             
50021              
50022         }else{
50023             //this.onEmptyResults();
50024         }
50025         //this.el.focus();
50026     },
50027     // private
50028     onLoadException : function()
50029     {
50030         dom.innerHTML = '';
50031             
50032         Roo.log("Select on load exception");
50033         return;
50034     
50035         this.collapse();
50036         Roo.log(this.store.reader.jsonData);
50037         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50038             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50039         }
50040         
50041         
50042     },
50043     // private
50044     onTypeAhead : function(){
50045          
50046     },
50047
50048     // private
50049     onSelect : function(record, index){
50050         Roo.log('on select?');
50051         return;
50052         if(this.fireEvent('beforeselect', this, record, index) !== false){
50053             this.setFromData(index > -1 ? record.data : false);
50054             this.collapse();
50055             this.fireEvent('select', this, record, index);
50056         }
50057     },
50058
50059     /**
50060      * Returns the currently selected field value or empty string if no value is set.
50061      * @return {String} value The selected value
50062      */
50063     getValue : function(){
50064         var dom = this.el.dom;
50065         this.value = dom.options[dom.selectedIndex].value;
50066         return this.value;
50067         
50068     },
50069
50070     /**
50071      * Clears any text/value currently set in the field
50072      */
50073     clearValue : function(){
50074         this.value = '';
50075         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50076         
50077     },
50078
50079     /**
50080      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50081      * will be displayed in the field.  If the value does not match the data value of an existing item,
50082      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50083      * Otherwise the field will be blank (although the value will still be set).
50084      * @param {String} value The value to match
50085      */
50086     setValue : function(v){
50087         var d = this.el.dom;
50088         for (var i =0; i < d.options.length;i++) {
50089             if (v == d.options[i].value) {
50090                 d.selectedIndex = i;
50091                 this.value = v;
50092                 return;
50093             }
50094         }
50095         this.clearValue();
50096     },
50097     /**
50098      * @property {Object} the last set data for the element
50099      */
50100     
50101     lastData : false,
50102     /**
50103      * Sets the value of the field based on a object which is related to the record format for the store.
50104      * @param {Object} value the value to set as. or false on reset?
50105      */
50106     setFromData : function(o){
50107         Roo.log('setfrom data?');
50108          
50109         
50110         
50111     },
50112     // private
50113     reset : function(){
50114         this.clearValue();
50115     },
50116     // private
50117     findRecord : function(prop, value){
50118         
50119         return false;
50120     
50121         var record;
50122         if(this.store.getCount() > 0){
50123             this.store.each(function(r){
50124                 if(r.data[prop] == value){
50125                     record = r;
50126                     return false;
50127                 }
50128                 return true;
50129             });
50130         }
50131         return record;
50132     },
50133     
50134     getName: function()
50135     {
50136         // returns hidden if it's set..
50137         if (!this.rendered) {return ''};
50138         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50139         
50140     },
50141      
50142
50143     
50144
50145     // private
50146     onEmptyResults : function(){
50147         Roo.log('empty results');
50148         //this.collapse();
50149     },
50150
50151     /**
50152      * Returns true if the dropdown list is expanded, else false.
50153      */
50154     isExpanded : function(){
50155         return false;
50156     },
50157
50158     /**
50159      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
50160      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50161      * @param {String} value The data value of the item to select
50162      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50163      * selected item if it is not currently in view (defaults to true)
50164      * @return {Boolean} True if the value matched an item in the list, else false
50165      */
50166     selectByValue : function(v, scrollIntoView){
50167         Roo.log('select By Value');
50168         return false;
50169     
50170         if(v !== undefined && v !== null){
50171             var r = this.findRecord(this.valueField || this.displayField, v);
50172             if(r){
50173                 this.select(this.store.indexOf(r), scrollIntoView);
50174                 return true;
50175             }
50176         }
50177         return false;
50178     },
50179
50180     /**
50181      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
50182      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
50183      * @param {Number} index The zero-based index of the list item to select
50184      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
50185      * selected item if it is not currently in view (defaults to true)
50186      */
50187     select : function(index, scrollIntoView){
50188         Roo.log('select ');
50189         return  ;
50190         
50191         this.selectedIndex = index;
50192         this.view.select(index);
50193         if(scrollIntoView !== false){
50194             var el = this.view.getNode(index);
50195             if(el){
50196                 this.innerList.scrollChildIntoView(el, false);
50197             }
50198         }
50199     },
50200
50201       
50202
50203     // private
50204     validateBlur : function(){
50205         
50206         return;
50207         
50208     },
50209
50210     // private
50211     initQuery : function(){
50212         this.doQuery(this.getRawValue());
50213     },
50214
50215     // private
50216     doForce : function(){
50217         if(this.el.dom.value.length > 0){
50218             this.el.dom.value =
50219                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
50220              
50221         }
50222     },
50223
50224     /**
50225      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
50226      * query allowing the query action to be canceled if needed.
50227      * @param {String} query The SQL query to execute
50228      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
50229      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
50230      * saved in the current store (defaults to false)
50231      */
50232     doQuery : function(q, forceAll){
50233         
50234         Roo.log('doQuery?');
50235         if(q === undefined || q === null){
50236             q = '';
50237         }
50238         var qe = {
50239             query: q,
50240             forceAll: forceAll,
50241             combo: this,
50242             cancel:false
50243         };
50244         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
50245             return false;
50246         }
50247         q = qe.query;
50248         forceAll = qe.forceAll;
50249         if(forceAll === true || (q.length >= this.minChars)){
50250             if(this.lastQuery != q || this.alwaysQuery){
50251                 this.lastQuery = q;
50252                 if(this.mode == 'local'){
50253                     this.selectedIndex = -1;
50254                     if(forceAll){
50255                         this.store.clearFilter();
50256                     }else{
50257                         this.store.filter(this.displayField, q);
50258                     }
50259                     this.onLoad();
50260                 }else{
50261                     this.store.baseParams[this.queryParam] = q;
50262                     this.store.load({
50263                         params: this.getParams(q)
50264                     });
50265                     this.expand();
50266                 }
50267             }else{
50268                 this.selectedIndex = -1;
50269                 this.onLoad();   
50270             }
50271         }
50272     },
50273
50274     // private
50275     getParams : function(q){
50276         var p = {};
50277         //p[this.queryParam] = q;
50278         if(this.pageSize){
50279             p.start = 0;
50280             p.limit = this.pageSize;
50281         }
50282         return p;
50283     },
50284
50285     /**
50286      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
50287      */
50288     collapse : function(){
50289         
50290     },
50291
50292     // private
50293     collapseIf : function(e){
50294         
50295     },
50296
50297     /**
50298      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
50299      */
50300     expand : function(){
50301         
50302     } ,
50303
50304     // private
50305      
50306
50307     /** 
50308     * @cfg {Boolean} grow 
50309     * @hide 
50310     */
50311     /** 
50312     * @cfg {Number} growMin 
50313     * @hide 
50314     */
50315     /** 
50316     * @cfg {Number} growMax 
50317     * @hide 
50318     */
50319     /**
50320      * @hide
50321      * @method autoSize
50322      */
50323     
50324     setWidth : function()
50325     {
50326         
50327     },
50328     getResizeEl : function(){
50329         return this.el;
50330     }
50331 });//<script type="text/javasscript">
50332  
50333
50334 /**
50335  * @class Roo.DDView
50336  * A DnD enabled version of Roo.View.
50337  * @param {Element/String} container The Element in which to create the View.
50338  * @param {String} tpl The template string used to create the markup for each element of the View
50339  * @param {Object} config The configuration properties. These include all the config options of
50340  * {@link Roo.View} plus some specific to this class.<br>
50341  * <p>
50342  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
50343  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
50344  * <p>
50345  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
50346 .x-view-drag-insert-above {
50347         border-top:1px dotted #3366cc;
50348 }
50349 .x-view-drag-insert-below {
50350         border-bottom:1px dotted #3366cc;
50351 }
50352 </code></pre>
50353  * 
50354  */
50355  
50356 Roo.DDView = function(container, tpl, config) {
50357     Roo.DDView.superclass.constructor.apply(this, arguments);
50358     this.getEl().setStyle("outline", "0px none");
50359     this.getEl().unselectable();
50360     if (this.dragGroup) {
50361                 this.setDraggable(this.dragGroup.split(","));
50362     }
50363     if (this.dropGroup) {
50364                 this.setDroppable(this.dropGroup.split(","));
50365     }
50366     if (this.deletable) {
50367         this.setDeletable();
50368     }
50369     this.isDirtyFlag = false;
50370         this.addEvents({
50371                 "drop" : true
50372         });
50373 };
50374
50375 Roo.extend(Roo.DDView, Roo.View, {
50376 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
50377 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
50378 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
50379 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
50380
50381         isFormField: true,
50382
50383         reset: Roo.emptyFn,
50384         
50385         clearInvalid: Roo.form.Field.prototype.clearInvalid,
50386
50387         validate: function() {
50388                 return true;
50389         },
50390         
50391         destroy: function() {
50392                 this.purgeListeners();
50393                 this.getEl.removeAllListeners();
50394                 this.getEl().remove();
50395                 if (this.dragZone) {
50396                         if (this.dragZone.destroy) {
50397                                 this.dragZone.destroy();
50398                         }
50399                 }
50400                 if (this.dropZone) {
50401                         if (this.dropZone.destroy) {
50402                                 this.dropZone.destroy();
50403                         }
50404                 }
50405         },
50406
50407 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
50408         getName: function() {
50409                 return this.name;
50410         },
50411
50412 /**     Loads the View from a JSON string representing the Records to put into the Store. */
50413         setValue: function(v) {
50414                 if (!this.store) {
50415                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
50416                 }
50417                 var data = {};
50418                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
50419                 this.store.proxy = new Roo.data.MemoryProxy(data);
50420                 this.store.load();
50421         },
50422
50423 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
50424         getValue: function() {
50425                 var result = '(';
50426                 this.store.each(function(rec) {
50427                         result += rec.id + ',';
50428                 });
50429                 return result.substr(0, result.length - 1) + ')';
50430         },
50431         
50432         getIds: function() {
50433                 var i = 0, result = new Array(this.store.getCount());
50434                 this.store.each(function(rec) {
50435                         result[i++] = rec.id;
50436                 });
50437                 return result;
50438         },
50439         
50440         isDirty: function() {
50441                 return this.isDirtyFlag;
50442         },
50443
50444 /**
50445  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
50446  *      whole Element becomes the target, and this causes the drop gesture to append.
50447  */
50448     getTargetFromEvent : function(e) {
50449                 var target = e.getTarget();
50450                 while ((target !== null) && (target.parentNode != this.el.dom)) {
50451                 target = target.parentNode;
50452                 }
50453                 if (!target) {
50454                         target = this.el.dom.lastChild || this.el.dom;
50455                 }
50456                 return target;
50457     },
50458
50459 /**
50460  *      Create the drag data which consists of an object which has the property "ddel" as
50461  *      the drag proxy element. 
50462  */
50463     getDragData : function(e) {
50464         var target = this.findItemFromChild(e.getTarget());
50465                 if(target) {
50466                         this.handleSelection(e);
50467                         var selNodes = this.getSelectedNodes();
50468             var dragData = {
50469                 source: this,
50470                 copy: this.copy || (this.allowCopy && e.ctrlKey),
50471                 nodes: selNodes,
50472                 records: []
50473                         };
50474                         var selectedIndices = this.getSelectedIndexes();
50475                         for (var i = 0; i < selectedIndices.length; i++) {
50476                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
50477                         }
50478                         if (selNodes.length == 1) {
50479                                 dragData.ddel = target.cloneNode(true); // the div element
50480                         } else {
50481                                 var div = document.createElement('div'); // create the multi element drag "ghost"
50482                                 div.className = 'multi-proxy';
50483                                 for (var i = 0, len = selNodes.length; i < len; i++) {
50484                                         div.appendChild(selNodes[i].cloneNode(true));
50485                                 }
50486                                 dragData.ddel = div;
50487                         }
50488             //console.log(dragData)
50489             //console.log(dragData.ddel.innerHTML)
50490                         return dragData;
50491                 }
50492         //console.log('nodragData')
50493                 return false;
50494     },
50495     
50496 /**     Specify to which ddGroup items in this DDView may be dragged. */
50497     setDraggable: function(ddGroup) {
50498         if (ddGroup instanceof Array) {
50499                 Roo.each(ddGroup, this.setDraggable, this);
50500                 return;
50501         }
50502         if (this.dragZone) {
50503                 this.dragZone.addToGroup(ddGroup);
50504         } else {
50505                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
50506                                 containerScroll: true,
50507                                 ddGroup: ddGroup 
50508
50509                         });
50510 //                      Draggability implies selection. DragZone's mousedown selects the element.
50511                         if (!this.multiSelect) { this.singleSelect = true; }
50512
50513 //                      Wire the DragZone's handlers up to methods in *this*
50514                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
50515                 }
50516     },
50517
50518 /**     Specify from which ddGroup this DDView accepts drops. */
50519     setDroppable: function(ddGroup) {
50520         if (ddGroup instanceof Array) {
50521                 Roo.each(ddGroup, this.setDroppable, this);
50522                 return;
50523         }
50524         if (this.dropZone) {
50525                 this.dropZone.addToGroup(ddGroup);
50526         } else {
50527                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
50528                                 containerScroll: true,
50529                                 ddGroup: ddGroup
50530                         });
50531
50532 //                      Wire the DropZone's handlers up to methods in *this*
50533                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
50534                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
50535                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
50536                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
50537                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
50538                 }
50539     },
50540
50541 /**     Decide whether to drop above or below a View node. */
50542     getDropPoint : function(e, n, dd){
50543         if (n == this.el.dom) { return "above"; }
50544                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
50545                 var c = t + (b - t) / 2;
50546                 var y = Roo.lib.Event.getPageY(e);
50547                 if(y <= c) {
50548                         return "above";
50549                 }else{
50550                         return "below";
50551                 }
50552     },
50553
50554     onNodeEnter : function(n, dd, e, data){
50555                 return false;
50556     },
50557     
50558     onNodeOver : function(n, dd, e, data){
50559                 var pt = this.getDropPoint(e, n, dd);
50560                 // set the insert point style on the target node
50561                 var dragElClass = this.dropNotAllowed;
50562                 if (pt) {
50563                         var targetElClass;
50564                         if (pt == "above"){
50565                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
50566                                 targetElClass = "x-view-drag-insert-above";
50567                         } else {
50568                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
50569                                 targetElClass = "x-view-drag-insert-below";
50570                         }
50571                         if (this.lastInsertClass != targetElClass){
50572                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
50573                                 this.lastInsertClass = targetElClass;
50574                         }
50575                 }
50576                 return dragElClass;
50577         },
50578
50579     onNodeOut : function(n, dd, e, data){
50580                 this.removeDropIndicators(n);
50581     },
50582
50583     onNodeDrop : function(n, dd, e, data){
50584         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
50585                 return false;
50586         }
50587         var pt = this.getDropPoint(e, n, dd);
50588                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
50589                 if (pt == "below") { insertAt++; }
50590                 for (var i = 0; i < data.records.length; i++) {
50591                         var r = data.records[i];
50592                         var dup = this.store.getById(r.id);
50593                         if (dup && (dd != this.dragZone)) {
50594                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
50595                         } else {
50596                                 if (data.copy) {
50597                                         this.store.insert(insertAt++, r.copy());
50598                                 } else {
50599                                         data.source.isDirtyFlag = true;
50600                                         r.store.remove(r);
50601                                         this.store.insert(insertAt++, r);
50602                                 }
50603                                 this.isDirtyFlag = true;
50604                         }
50605                 }
50606                 this.dragZone.cachedTarget = null;
50607                 return true;
50608     },
50609
50610     removeDropIndicators : function(n){
50611                 if(n){
50612                         Roo.fly(n).removeClass([
50613                                 "x-view-drag-insert-above",
50614                                 "x-view-drag-insert-below"]);
50615                         this.lastInsertClass = "_noclass";
50616                 }
50617     },
50618
50619 /**
50620  *      Utility method. Add a delete option to the DDView's context menu.
50621  *      @param {String} imageUrl The URL of the "delete" icon image.
50622  */
50623         setDeletable: function(imageUrl) {
50624                 if (!this.singleSelect && !this.multiSelect) {
50625                         this.singleSelect = true;
50626                 }
50627                 var c = this.getContextMenu();
50628                 this.contextMenu.on("itemclick", function(item) {
50629                         switch (item.id) {
50630                                 case "delete":
50631                                         this.remove(this.getSelectedIndexes());
50632                                         break;
50633                         }
50634                 }, this);
50635                 this.contextMenu.add({
50636                         icon: imageUrl,
50637                         id: "delete",
50638                         text: 'Delete'
50639                 });
50640         },
50641         
50642 /**     Return the context menu for this DDView. */
50643         getContextMenu: function() {
50644                 if (!this.contextMenu) {
50645 //                      Create the View's context menu
50646                         this.contextMenu = new Roo.menu.Menu({
50647                                 id: this.id + "-contextmenu"
50648                         });
50649                         this.el.on("contextmenu", this.showContextMenu, this);
50650                 }
50651                 return this.contextMenu;
50652         },
50653         
50654         disableContextMenu: function() {
50655                 if (this.contextMenu) {
50656                         this.el.un("contextmenu", this.showContextMenu, this);
50657                 }
50658         },
50659
50660         showContextMenu: function(e, item) {
50661         item = this.findItemFromChild(e.getTarget());
50662                 if (item) {
50663                         e.stopEvent();
50664                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
50665                         this.contextMenu.showAt(e.getXY());
50666             }
50667     },
50668
50669 /**
50670  *      Remove {@link Roo.data.Record}s at the specified indices.
50671  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
50672  */
50673     remove: function(selectedIndices) {
50674                 selectedIndices = [].concat(selectedIndices);
50675                 for (var i = 0; i < selectedIndices.length; i++) {
50676                         var rec = this.store.getAt(selectedIndices[i]);
50677                         this.store.remove(rec);
50678                 }
50679     },
50680
50681 /**
50682  *      Double click fires the event, but also, if this is draggable, and there is only one other
50683  *      related DropZone, it transfers the selected node.
50684  */
50685     onDblClick : function(e){
50686         var item = this.findItemFromChild(e.getTarget());
50687         if(item){
50688             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
50689                 return false;
50690             }
50691             if (this.dragGroup) {
50692                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
50693                     while (targets.indexOf(this.dropZone) > -1) {
50694                             targets.remove(this.dropZone);
50695                                 }
50696                     if (targets.length == 1) {
50697                                         this.dragZone.cachedTarget = null;
50698                         var el = Roo.get(targets[0].getEl());
50699                         var box = el.getBox(true);
50700                         targets[0].onNodeDrop(el.dom, {
50701                                 target: el.dom,
50702                                 xy: [box.x, box.y + box.height - 1]
50703                         }, null, this.getDragData(e));
50704                     }
50705                 }
50706         }
50707     },
50708     
50709     handleSelection: function(e) {
50710                 this.dragZone.cachedTarget = null;
50711         var item = this.findItemFromChild(e.getTarget());
50712         if (!item) {
50713                 this.clearSelections(true);
50714                 return;
50715         }
50716                 if (item && (this.multiSelect || this.singleSelect)){
50717                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
50718                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
50719                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
50720                                 this.unselect(item);
50721                         } else {
50722                                 this.select(item, this.multiSelect && e.ctrlKey);
50723                                 this.lastSelection = item;
50724                         }
50725                 }
50726     },
50727
50728     onItemClick : function(item, index, e){
50729                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
50730                         return false;
50731                 }
50732                 return true;
50733     },
50734
50735     unselect : function(nodeInfo, suppressEvent){
50736                 var node = this.getNode(nodeInfo);
50737                 if(node && this.isSelected(node)){
50738                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
50739                                 Roo.fly(node).removeClass(this.selectedClass);
50740                                 this.selections.remove(node);
50741                                 if(!suppressEvent){
50742                                         this.fireEvent("selectionchange", this, this.selections);
50743                                 }
50744                         }
50745                 }
50746     }
50747 });
50748 /*
50749  * Based on:
50750  * Ext JS Library 1.1.1
50751  * Copyright(c) 2006-2007, Ext JS, LLC.
50752  *
50753  * Originally Released Under LGPL - original licence link has changed is not relivant.
50754  *
50755  * Fork - LGPL
50756  * <script type="text/javascript">
50757  */
50758  
50759 /**
50760  * @class Roo.LayoutManager
50761  * @extends Roo.util.Observable
50762  * Base class for layout managers.
50763  */
50764 Roo.LayoutManager = function(container, config){
50765     Roo.LayoutManager.superclass.constructor.call(this);
50766     this.el = Roo.get(container);
50767     // ie scrollbar fix
50768     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
50769         document.body.scroll = "no";
50770     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
50771         this.el.position('relative');
50772     }
50773     this.id = this.el.id;
50774     this.el.addClass("x-layout-container");
50775     /** false to disable window resize monitoring @type Boolean */
50776     this.monitorWindowResize = true;
50777     this.regions = {};
50778     this.addEvents({
50779         /**
50780          * @event layout
50781          * Fires when a layout is performed. 
50782          * @param {Roo.LayoutManager} this
50783          */
50784         "layout" : true,
50785         /**
50786          * @event regionresized
50787          * Fires when the user resizes a region. 
50788          * @param {Roo.LayoutRegion} region The resized region
50789          * @param {Number} newSize The new size (width for east/west, height for north/south)
50790          */
50791         "regionresized" : true,
50792         /**
50793          * @event regioncollapsed
50794          * Fires when a region is collapsed. 
50795          * @param {Roo.LayoutRegion} region The collapsed region
50796          */
50797         "regioncollapsed" : true,
50798         /**
50799          * @event regionexpanded
50800          * Fires when a region is expanded.  
50801          * @param {Roo.LayoutRegion} region The expanded region
50802          */
50803         "regionexpanded" : true
50804     });
50805     this.updating = false;
50806     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
50807 };
50808
50809 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
50810     /**
50811      * Returns true if this layout is currently being updated
50812      * @return {Boolean}
50813      */
50814     isUpdating : function(){
50815         return this.updating; 
50816     },
50817     
50818     /**
50819      * Suspend the LayoutManager from doing auto-layouts while
50820      * making multiple add or remove calls
50821      */
50822     beginUpdate : function(){
50823         this.updating = true;    
50824     },
50825     
50826     /**
50827      * Restore auto-layouts and optionally disable the manager from performing a layout
50828      * @param {Boolean} noLayout true to disable a layout update 
50829      */
50830     endUpdate : function(noLayout){
50831         this.updating = false;
50832         if(!noLayout){
50833             this.layout();
50834         }    
50835     },
50836     
50837     layout: function(){
50838         
50839     },
50840     
50841     onRegionResized : function(region, newSize){
50842         this.fireEvent("regionresized", region, newSize);
50843         this.layout();
50844     },
50845     
50846     onRegionCollapsed : function(region){
50847         this.fireEvent("regioncollapsed", region);
50848     },
50849     
50850     onRegionExpanded : function(region){
50851         this.fireEvent("regionexpanded", region);
50852     },
50853         
50854     /**
50855      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
50856      * performs box-model adjustments.
50857      * @return {Object} The size as an object {width: (the width), height: (the height)}
50858      */
50859     getViewSize : function(){
50860         var size;
50861         if(this.el.dom != document.body){
50862             size = this.el.getSize();
50863         }else{
50864             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
50865         }
50866         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
50867         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
50868         return size;
50869     },
50870     
50871     /**
50872      * Returns the Element this layout is bound to.
50873      * @return {Roo.Element}
50874      */
50875     getEl : function(){
50876         return this.el;
50877     },
50878     
50879     /**
50880      * Returns the specified region.
50881      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
50882      * @return {Roo.LayoutRegion}
50883      */
50884     getRegion : function(target){
50885         return this.regions[target.toLowerCase()];
50886     },
50887     
50888     onWindowResize : function(){
50889         if(this.monitorWindowResize){
50890             this.layout();
50891         }
50892     }
50893 });/*
50894  * Based on:
50895  * Ext JS Library 1.1.1
50896  * Copyright(c) 2006-2007, Ext JS, LLC.
50897  *
50898  * Originally Released Under LGPL - original licence link has changed is not relivant.
50899  *
50900  * Fork - LGPL
50901  * <script type="text/javascript">
50902  */
50903 /**
50904  * @class Roo.BorderLayout
50905  * @extends Roo.LayoutManager
50906  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
50907  * please see: <br><br>
50908  * <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>
50909  * <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>
50910  * Example:
50911  <pre><code>
50912  var layout = new Roo.BorderLayout(document.body, {
50913     north: {
50914         initialSize: 25,
50915         titlebar: false
50916     },
50917     west: {
50918         split:true,
50919         initialSize: 200,
50920         minSize: 175,
50921         maxSize: 400,
50922         titlebar: true,
50923         collapsible: true
50924     },
50925     east: {
50926         split:true,
50927         initialSize: 202,
50928         minSize: 175,
50929         maxSize: 400,
50930         titlebar: true,
50931         collapsible: true
50932     },
50933     south: {
50934         split:true,
50935         initialSize: 100,
50936         minSize: 100,
50937         maxSize: 200,
50938         titlebar: true,
50939         collapsible: true
50940     },
50941     center: {
50942         titlebar: true,
50943         autoScroll:true,
50944         resizeTabs: true,
50945         minTabWidth: 50,
50946         preferredTabWidth: 150
50947     }
50948 });
50949
50950 // shorthand
50951 var CP = Roo.ContentPanel;
50952
50953 layout.beginUpdate();
50954 layout.add("north", new CP("north", "North"));
50955 layout.add("south", new CP("south", {title: "South", closable: true}));
50956 layout.add("west", new CP("west", {title: "West"}));
50957 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
50958 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
50959 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
50960 layout.getRegion("center").showPanel("center1");
50961 layout.endUpdate();
50962 </code></pre>
50963
50964 <b>The container the layout is rendered into can be either the body element or any other element.
50965 If it is not the body element, the container needs to either be an absolute positioned element,
50966 or you will need to add "position:relative" to the css of the container.  You will also need to specify
50967 the container size if it is not the body element.</b>
50968
50969 * @constructor
50970 * Create a new BorderLayout
50971 * @param {String/HTMLElement/Element} container The container this layout is bound to
50972 * @param {Object} config Configuration options
50973  */
50974 Roo.BorderLayout = function(container, config){
50975     config = config || {};
50976     Roo.BorderLayout.superclass.constructor.call(this, container, config);
50977     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
50978     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
50979         var target = this.factory.validRegions[i];
50980         if(config[target]){
50981             this.addRegion(target, config[target]);
50982         }
50983     }
50984 };
50985
50986 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
50987     /**
50988      * Creates and adds a new region if it doesn't already exist.
50989      * @param {String} target The target region key (north, south, east, west or center).
50990      * @param {Object} config The regions config object
50991      * @return {BorderLayoutRegion} The new region
50992      */
50993     addRegion : function(target, config){
50994         if(!this.regions[target]){
50995             var r = this.factory.create(target, this, config);
50996             this.bindRegion(target, r);
50997         }
50998         return this.regions[target];
50999     },
51000
51001     // private (kinda)
51002     bindRegion : function(name, r){
51003         this.regions[name] = r;
51004         r.on("visibilitychange", this.layout, this);
51005         r.on("paneladded", this.layout, this);
51006         r.on("panelremoved", this.layout, this);
51007         r.on("invalidated", this.layout, this);
51008         r.on("resized", this.onRegionResized, this);
51009         r.on("collapsed", this.onRegionCollapsed, this);
51010         r.on("expanded", this.onRegionExpanded, this);
51011     },
51012
51013     /**
51014      * Performs a layout update.
51015      */
51016     layout : function(){
51017         if(this.updating) {
51018             return;
51019         }
51020         var size = this.getViewSize();
51021         var w = size.width;
51022         var h = size.height;
51023         var centerW = w;
51024         var centerH = h;
51025         var centerY = 0;
51026         var centerX = 0;
51027         //var x = 0, y = 0;
51028
51029         var rs = this.regions;
51030         var north = rs["north"];
51031         var south = rs["south"]; 
51032         var west = rs["west"];
51033         var east = rs["east"];
51034         var center = rs["center"];
51035         //if(this.hideOnLayout){ // not supported anymore
51036             //c.el.setStyle("display", "none");
51037         //}
51038         if(north && north.isVisible()){
51039             var b = north.getBox();
51040             var m = north.getMargins();
51041             b.width = w - (m.left+m.right);
51042             b.x = m.left;
51043             b.y = m.top;
51044             centerY = b.height + b.y + m.bottom;
51045             centerH -= centerY;
51046             north.updateBox(this.safeBox(b));
51047         }
51048         if(south && south.isVisible()){
51049             var b = south.getBox();
51050             var m = south.getMargins();
51051             b.width = w - (m.left+m.right);
51052             b.x = m.left;
51053             var totalHeight = (b.height + m.top + m.bottom);
51054             b.y = h - totalHeight + m.top;
51055             centerH -= totalHeight;
51056             south.updateBox(this.safeBox(b));
51057         }
51058         if(west && west.isVisible()){
51059             var b = west.getBox();
51060             var m = west.getMargins();
51061             b.height = centerH - (m.top+m.bottom);
51062             b.x = m.left;
51063             b.y = centerY + m.top;
51064             var totalWidth = (b.width + m.left + m.right);
51065             centerX += totalWidth;
51066             centerW -= totalWidth;
51067             west.updateBox(this.safeBox(b));
51068         }
51069         if(east && east.isVisible()){
51070             var b = east.getBox();
51071             var m = east.getMargins();
51072             b.height = centerH - (m.top+m.bottom);
51073             var totalWidth = (b.width + m.left + m.right);
51074             b.x = w - totalWidth + m.left;
51075             b.y = centerY + m.top;
51076             centerW -= totalWidth;
51077             east.updateBox(this.safeBox(b));
51078         }
51079         if(center){
51080             var m = center.getMargins();
51081             var centerBox = {
51082                 x: centerX + m.left,
51083                 y: centerY + m.top,
51084                 width: centerW - (m.left+m.right),
51085                 height: centerH - (m.top+m.bottom)
51086             };
51087             //if(this.hideOnLayout){
51088                 //center.el.setStyle("display", "block");
51089             //}
51090             center.updateBox(this.safeBox(centerBox));
51091         }
51092         this.el.repaint();
51093         this.fireEvent("layout", this);
51094     },
51095
51096     // private
51097     safeBox : function(box){
51098         box.width = Math.max(0, box.width);
51099         box.height = Math.max(0, box.height);
51100         return box;
51101     },
51102
51103     /**
51104      * Adds a ContentPanel (or subclass) to this layout.
51105      * @param {String} target The target region key (north, south, east, west or center).
51106      * @param {Roo.ContentPanel} panel The panel to add
51107      * @return {Roo.ContentPanel} The added panel
51108      */
51109     add : function(target, panel){
51110          
51111         target = target.toLowerCase();
51112         return this.regions[target].add(panel);
51113     },
51114
51115     /**
51116      * Remove a ContentPanel (or subclass) to this layout.
51117      * @param {String} target The target region key (north, south, east, west or center).
51118      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51119      * @return {Roo.ContentPanel} The removed panel
51120      */
51121     remove : function(target, panel){
51122         target = target.toLowerCase();
51123         return this.regions[target].remove(panel);
51124     },
51125
51126     /**
51127      * Searches all regions for a panel with the specified id
51128      * @param {String} panelId
51129      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51130      */
51131     findPanel : function(panelId){
51132         var rs = this.regions;
51133         for(var target in rs){
51134             if(typeof rs[target] != "function"){
51135                 var p = rs[target].getPanel(panelId);
51136                 if(p){
51137                     return p;
51138                 }
51139             }
51140         }
51141         return null;
51142     },
51143
51144     /**
51145      * Searches all regions for a panel with the specified id and activates (shows) it.
51146      * @param {String/ContentPanel} panelId The panels id or the panel itself
51147      * @return {Roo.ContentPanel} The shown panel or null
51148      */
51149     showPanel : function(panelId) {
51150       var rs = this.regions;
51151       for(var target in rs){
51152          var r = rs[target];
51153          if(typeof r != "function"){
51154             if(r.hasPanel(panelId)){
51155                return r.showPanel(panelId);
51156             }
51157          }
51158       }
51159       return null;
51160    },
51161
51162    /**
51163      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
51164      * @param {Roo.state.Provider} provider (optional) An alternate state provider
51165      */
51166     restoreState : function(provider){
51167         if(!provider){
51168             provider = Roo.state.Manager;
51169         }
51170         var sm = new Roo.LayoutStateManager();
51171         sm.init(this, provider);
51172     },
51173
51174     /**
51175      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
51176      * object should contain properties for each region to add ContentPanels to, and each property's value should be
51177      * a valid ContentPanel config object.  Example:
51178      * <pre><code>
51179 // Create the main layout
51180 var layout = new Roo.BorderLayout('main-ct', {
51181     west: {
51182         split:true,
51183         minSize: 175,
51184         titlebar: true
51185     },
51186     center: {
51187         title:'Components'
51188     }
51189 }, 'main-ct');
51190
51191 // Create and add multiple ContentPanels at once via configs
51192 layout.batchAdd({
51193    west: {
51194        id: 'source-files',
51195        autoCreate:true,
51196        title:'Ext Source Files',
51197        autoScroll:true,
51198        fitToFrame:true
51199    },
51200    center : {
51201        el: cview,
51202        autoScroll:true,
51203        fitToFrame:true,
51204        toolbar: tb,
51205        resizeEl:'cbody'
51206    }
51207 });
51208 </code></pre>
51209      * @param {Object} regions An object containing ContentPanel configs by region name
51210      */
51211     batchAdd : function(regions){
51212         this.beginUpdate();
51213         for(var rname in regions){
51214             var lr = this.regions[rname];
51215             if(lr){
51216                 this.addTypedPanels(lr, regions[rname]);
51217             }
51218         }
51219         this.endUpdate();
51220     },
51221
51222     // private
51223     addTypedPanels : function(lr, ps){
51224         if(typeof ps == 'string'){
51225             lr.add(new Roo.ContentPanel(ps));
51226         }
51227         else if(ps instanceof Array){
51228             for(var i =0, len = ps.length; i < len; i++){
51229                 this.addTypedPanels(lr, ps[i]);
51230             }
51231         }
51232         else if(!ps.events){ // raw config?
51233             var el = ps.el;
51234             delete ps.el; // prevent conflict
51235             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
51236         }
51237         else {  // panel object assumed!
51238             lr.add(ps);
51239         }
51240     },
51241     /**
51242      * Adds a xtype elements to the layout.
51243      * <pre><code>
51244
51245 layout.addxtype({
51246        xtype : 'ContentPanel',
51247        region: 'west',
51248        items: [ .... ]
51249    }
51250 );
51251
51252 layout.addxtype({
51253         xtype : 'NestedLayoutPanel',
51254         region: 'west',
51255         layout: {
51256            center: { },
51257            west: { }   
51258         },
51259         items : [ ... list of content panels or nested layout panels.. ]
51260    }
51261 );
51262 </code></pre>
51263      * @param {Object} cfg Xtype definition of item to add.
51264      */
51265     addxtype : function(cfg)
51266     {
51267         // basically accepts a pannel...
51268         // can accept a layout region..!?!?
51269         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
51270         
51271         if (!cfg.xtype.match(/Panel$/)) {
51272             return false;
51273         }
51274         var ret = false;
51275         
51276         if (typeof(cfg.region) == 'undefined') {
51277             Roo.log("Failed to add Panel, region was not set");
51278             Roo.log(cfg);
51279             return false;
51280         }
51281         var region = cfg.region;
51282         delete cfg.region;
51283         
51284           
51285         var xitems = [];
51286         if (cfg.items) {
51287             xitems = cfg.items;
51288             delete cfg.items;
51289         }
51290         var nb = false;
51291         
51292         switch(cfg.xtype) 
51293         {
51294             case 'ContentPanel':  // ContentPanel (el, cfg)
51295             case 'ScrollPanel':  // ContentPanel (el, cfg)
51296             case 'ViewPanel': 
51297                 if(cfg.autoCreate) {
51298                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51299                 } else {
51300                     var el = this.el.createChild();
51301                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
51302                 }
51303                 
51304                 this.add(region, ret);
51305                 break;
51306             
51307             
51308             case 'TreePanel': // our new panel!
51309                 cfg.el = this.el.createChild();
51310                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51311                 this.add(region, ret);
51312                 break;
51313             
51314             case 'NestedLayoutPanel': 
51315                 // create a new Layout (which is  a Border Layout...
51316                 var el = this.el.createChild();
51317                 var clayout = cfg.layout;
51318                 delete cfg.layout;
51319                 clayout.items   = clayout.items  || [];
51320                 // replace this exitems with the clayout ones..
51321                 xitems = clayout.items;
51322                  
51323                 
51324                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
51325                     cfg.background = false;
51326                 }
51327                 var layout = new Roo.BorderLayout(el, clayout);
51328                 
51329                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
51330                 //console.log('adding nested layout panel '  + cfg.toSource());
51331                 this.add(region, ret);
51332                 nb = {}; /// find first...
51333                 break;
51334                 
51335             case 'GridPanel': 
51336             
51337                 // needs grid and region
51338                 
51339                 //var el = this.getRegion(region).el.createChild();
51340                 var el = this.el.createChild();
51341                 // create the grid first...
51342                 
51343                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
51344                 delete cfg.grid;
51345                 if (region == 'center' && this.active ) {
51346                     cfg.background = false;
51347                 }
51348                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
51349                 
51350                 this.add(region, ret);
51351                 if (cfg.background) {
51352                     ret.on('activate', function(gp) {
51353                         if (!gp.grid.rendered) {
51354                             gp.grid.render();
51355                         }
51356                     });
51357                 } else {
51358                     grid.render();
51359                 }
51360                 break;
51361            
51362            
51363            
51364                 
51365                 
51366                 
51367             default:
51368                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
51369                     
51370                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
51371                     this.add(region, ret);
51372                 } else {
51373                 
51374                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
51375                     return null;
51376                 }
51377                 
51378              // GridPanel (grid, cfg)
51379             
51380         }
51381         this.beginUpdate();
51382         // add children..
51383         var region = '';
51384         var abn = {};
51385         Roo.each(xitems, function(i)  {
51386             region = nb && i.region ? i.region : false;
51387             
51388             var add = ret.addxtype(i);
51389            
51390             if (region) {
51391                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
51392                 if (!i.background) {
51393                     abn[region] = nb[region] ;
51394                 }
51395             }
51396             
51397         });
51398         this.endUpdate();
51399
51400         // make the last non-background panel active..
51401         //if (nb) { Roo.log(abn); }
51402         if (nb) {
51403             
51404             for(var r in abn) {
51405                 region = this.getRegion(r);
51406                 if (region) {
51407                     // tried using nb[r], but it does not work..
51408                      
51409                     region.showPanel(abn[r]);
51410                    
51411                 }
51412             }
51413         }
51414         return ret;
51415         
51416     }
51417 });
51418
51419 /**
51420  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
51421  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
51422  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
51423  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
51424  * <pre><code>
51425 // shorthand
51426 var CP = Roo.ContentPanel;
51427
51428 var layout = Roo.BorderLayout.create({
51429     north: {
51430         initialSize: 25,
51431         titlebar: false,
51432         panels: [new CP("north", "North")]
51433     },
51434     west: {
51435         split:true,
51436         initialSize: 200,
51437         minSize: 175,
51438         maxSize: 400,
51439         titlebar: true,
51440         collapsible: true,
51441         panels: [new CP("west", {title: "West"})]
51442     },
51443     east: {
51444         split:true,
51445         initialSize: 202,
51446         minSize: 175,
51447         maxSize: 400,
51448         titlebar: true,
51449         collapsible: true,
51450         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
51451     },
51452     south: {
51453         split:true,
51454         initialSize: 100,
51455         minSize: 100,
51456         maxSize: 200,
51457         titlebar: true,
51458         collapsible: true,
51459         panels: [new CP("south", {title: "South", closable: true})]
51460     },
51461     center: {
51462         titlebar: true,
51463         autoScroll:true,
51464         resizeTabs: true,
51465         minTabWidth: 50,
51466         preferredTabWidth: 150,
51467         panels: [
51468             new CP("center1", {title: "Close Me", closable: true}),
51469             new CP("center2", {title: "Center Panel", closable: false})
51470         ]
51471     }
51472 }, document.body);
51473
51474 layout.getRegion("center").showPanel("center1");
51475 </code></pre>
51476  * @param config
51477  * @param targetEl
51478  */
51479 Roo.BorderLayout.create = function(config, targetEl){
51480     var layout = new Roo.BorderLayout(targetEl || document.body, config);
51481     layout.beginUpdate();
51482     var regions = Roo.BorderLayout.RegionFactory.validRegions;
51483     for(var j = 0, jlen = regions.length; j < jlen; j++){
51484         var lr = regions[j];
51485         if(layout.regions[lr] && config[lr].panels){
51486             var r = layout.regions[lr];
51487             var ps = config[lr].panels;
51488             layout.addTypedPanels(r, ps);
51489         }
51490     }
51491     layout.endUpdate();
51492     return layout;
51493 };
51494
51495 // private
51496 Roo.BorderLayout.RegionFactory = {
51497     // private
51498     validRegions : ["north","south","east","west","center"],
51499
51500     // private
51501     create : function(target, mgr, config){
51502         target = target.toLowerCase();
51503         if(config.lightweight || config.basic){
51504             return new Roo.BasicLayoutRegion(mgr, config, target);
51505         }
51506         switch(target){
51507             case "north":
51508                 return new Roo.NorthLayoutRegion(mgr, config);
51509             case "south":
51510                 return new Roo.SouthLayoutRegion(mgr, config);
51511             case "east":
51512                 return new Roo.EastLayoutRegion(mgr, config);
51513             case "west":
51514                 return new Roo.WestLayoutRegion(mgr, config);
51515             case "center":
51516                 return new Roo.CenterLayoutRegion(mgr, config);
51517         }
51518         throw 'Layout region "'+target+'" not supported.';
51519     }
51520 };/*
51521  * Based on:
51522  * Ext JS Library 1.1.1
51523  * Copyright(c) 2006-2007, Ext JS, LLC.
51524  *
51525  * Originally Released Under LGPL - original licence link has changed is not relivant.
51526  *
51527  * Fork - LGPL
51528  * <script type="text/javascript">
51529  */
51530  
51531 /**
51532  * @class Roo.BasicLayoutRegion
51533  * @extends Roo.util.Observable
51534  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
51535  * and does not have a titlebar, tabs or any other features. All it does is size and position 
51536  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
51537  */
51538 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
51539     this.mgr = mgr;
51540     this.position  = pos;
51541     this.events = {
51542         /**
51543          * @scope Roo.BasicLayoutRegion
51544          */
51545         
51546         /**
51547          * @event beforeremove
51548          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
51549          * @param {Roo.LayoutRegion} this
51550          * @param {Roo.ContentPanel} panel The panel
51551          * @param {Object} e The cancel event object
51552          */
51553         "beforeremove" : true,
51554         /**
51555          * @event invalidated
51556          * Fires when the layout for this region is changed.
51557          * @param {Roo.LayoutRegion} this
51558          */
51559         "invalidated" : true,
51560         /**
51561          * @event visibilitychange
51562          * Fires when this region is shown or hidden 
51563          * @param {Roo.LayoutRegion} this
51564          * @param {Boolean} visibility true or false
51565          */
51566         "visibilitychange" : true,
51567         /**
51568          * @event paneladded
51569          * Fires when a panel is added. 
51570          * @param {Roo.LayoutRegion} this
51571          * @param {Roo.ContentPanel} panel The panel
51572          */
51573         "paneladded" : true,
51574         /**
51575          * @event panelremoved
51576          * Fires when a panel is removed. 
51577          * @param {Roo.LayoutRegion} this
51578          * @param {Roo.ContentPanel} panel The panel
51579          */
51580         "panelremoved" : true,
51581         /**
51582          * @event beforecollapse
51583          * Fires when this region before collapse.
51584          * @param {Roo.LayoutRegion} this
51585          */
51586         "beforecollapse" : true,
51587         /**
51588          * @event collapsed
51589          * Fires when this region is collapsed.
51590          * @param {Roo.LayoutRegion} this
51591          */
51592         "collapsed" : true,
51593         /**
51594          * @event expanded
51595          * Fires when this region is expanded.
51596          * @param {Roo.LayoutRegion} this
51597          */
51598         "expanded" : true,
51599         /**
51600          * @event slideshow
51601          * Fires when this region is slid into view.
51602          * @param {Roo.LayoutRegion} this
51603          */
51604         "slideshow" : true,
51605         /**
51606          * @event slidehide
51607          * Fires when this region slides out of view. 
51608          * @param {Roo.LayoutRegion} this
51609          */
51610         "slidehide" : true,
51611         /**
51612          * @event panelactivated
51613          * Fires when a panel is activated. 
51614          * @param {Roo.LayoutRegion} this
51615          * @param {Roo.ContentPanel} panel The activated panel
51616          */
51617         "panelactivated" : true,
51618         /**
51619          * @event resized
51620          * Fires when the user resizes this region. 
51621          * @param {Roo.LayoutRegion} this
51622          * @param {Number} newSize The new size (width for east/west, height for north/south)
51623          */
51624         "resized" : true
51625     };
51626     /** A collection of panels in this region. @type Roo.util.MixedCollection */
51627     this.panels = new Roo.util.MixedCollection();
51628     this.panels.getKey = this.getPanelId.createDelegate(this);
51629     this.box = null;
51630     this.activePanel = null;
51631     // ensure listeners are added...
51632     
51633     if (config.listeners || config.events) {
51634         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
51635             listeners : config.listeners || {},
51636             events : config.events || {}
51637         });
51638     }
51639     
51640     if(skipConfig !== true){
51641         this.applyConfig(config);
51642     }
51643 };
51644
51645 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
51646     getPanelId : function(p){
51647         return p.getId();
51648     },
51649     
51650     applyConfig : function(config){
51651         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51652         this.config = config;
51653         
51654     },
51655     
51656     /**
51657      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
51658      * the width, for horizontal (north, south) the height.
51659      * @param {Number} newSize The new width or height
51660      */
51661     resizeTo : function(newSize){
51662         var el = this.el ? this.el :
51663                  (this.activePanel ? this.activePanel.getEl() : null);
51664         if(el){
51665             switch(this.position){
51666                 case "east":
51667                 case "west":
51668                     el.setWidth(newSize);
51669                     this.fireEvent("resized", this, newSize);
51670                 break;
51671                 case "north":
51672                 case "south":
51673                     el.setHeight(newSize);
51674                     this.fireEvent("resized", this, newSize);
51675                 break;                
51676             }
51677         }
51678     },
51679     
51680     getBox : function(){
51681         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
51682     },
51683     
51684     getMargins : function(){
51685         return this.margins;
51686     },
51687     
51688     updateBox : function(box){
51689         this.box = box;
51690         var el = this.activePanel.getEl();
51691         el.dom.style.left = box.x + "px";
51692         el.dom.style.top = box.y + "px";
51693         this.activePanel.setSize(box.width, box.height);
51694     },
51695     
51696     /**
51697      * Returns the container element for this region.
51698      * @return {Roo.Element}
51699      */
51700     getEl : function(){
51701         return this.activePanel;
51702     },
51703     
51704     /**
51705      * Returns true if this region is currently visible.
51706      * @return {Boolean}
51707      */
51708     isVisible : function(){
51709         return this.activePanel ? true : false;
51710     },
51711     
51712     setActivePanel : function(panel){
51713         panel = this.getPanel(panel);
51714         if(this.activePanel && this.activePanel != panel){
51715             this.activePanel.setActiveState(false);
51716             this.activePanel.getEl().setLeftTop(-10000,-10000);
51717         }
51718         this.activePanel = panel;
51719         panel.setActiveState(true);
51720         if(this.box){
51721             panel.setSize(this.box.width, this.box.height);
51722         }
51723         this.fireEvent("panelactivated", this, panel);
51724         this.fireEvent("invalidated");
51725     },
51726     
51727     /**
51728      * Show the specified panel.
51729      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
51730      * @return {Roo.ContentPanel} The shown panel or null
51731      */
51732     showPanel : function(panel){
51733         if(panel = this.getPanel(panel)){
51734             this.setActivePanel(panel);
51735         }
51736         return panel;
51737     },
51738     
51739     /**
51740      * Get the active panel for this region.
51741      * @return {Roo.ContentPanel} The active panel or null
51742      */
51743     getActivePanel : function(){
51744         return this.activePanel;
51745     },
51746     
51747     /**
51748      * Add the passed ContentPanel(s)
51749      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
51750      * @return {Roo.ContentPanel} The panel added (if only one was added)
51751      */
51752     add : function(panel){
51753         if(arguments.length > 1){
51754             for(var i = 0, len = arguments.length; i < len; i++) {
51755                 this.add(arguments[i]);
51756             }
51757             return null;
51758         }
51759         if(this.hasPanel(panel)){
51760             this.showPanel(panel);
51761             return panel;
51762         }
51763         var el = panel.getEl();
51764         if(el.dom.parentNode != this.mgr.el.dom){
51765             this.mgr.el.dom.appendChild(el.dom);
51766         }
51767         if(panel.setRegion){
51768             panel.setRegion(this);
51769         }
51770         this.panels.add(panel);
51771         el.setStyle("position", "absolute");
51772         if(!panel.background){
51773             this.setActivePanel(panel);
51774             if(this.config.initialSize && this.panels.getCount()==1){
51775                 this.resizeTo(this.config.initialSize);
51776             }
51777         }
51778         this.fireEvent("paneladded", this, panel);
51779         return panel;
51780     },
51781     
51782     /**
51783      * Returns true if the panel is in this region.
51784      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51785      * @return {Boolean}
51786      */
51787     hasPanel : function(panel){
51788         if(typeof panel == "object"){ // must be panel obj
51789             panel = panel.getId();
51790         }
51791         return this.getPanel(panel) ? true : false;
51792     },
51793     
51794     /**
51795      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
51796      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51797      * @param {Boolean} preservePanel Overrides the config preservePanel option
51798      * @return {Roo.ContentPanel} The panel that was removed
51799      */
51800     remove : function(panel, preservePanel){
51801         panel = this.getPanel(panel);
51802         if(!panel){
51803             return null;
51804         }
51805         var e = {};
51806         this.fireEvent("beforeremove", this, panel, e);
51807         if(e.cancel === true){
51808             return null;
51809         }
51810         var panelId = panel.getId();
51811         this.panels.removeKey(panelId);
51812         return panel;
51813     },
51814     
51815     /**
51816      * Returns the panel specified or null if it's not in this region.
51817      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
51818      * @return {Roo.ContentPanel}
51819      */
51820     getPanel : function(id){
51821         if(typeof id == "object"){ // must be panel obj
51822             return id;
51823         }
51824         return this.panels.get(id);
51825     },
51826     
51827     /**
51828      * Returns this regions position (north/south/east/west/center).
51829      * @return {String} 
51830      */
51831     getPosition: function(){
51832         return this.position;    
51833     }
51834 });/*
51835  * Based on:
51836  * Ext JS Library 1.1.1
51837  * Copyright(c) 2006-2007, Ext JS, LLC.
51838  *
51839  * Originally Released Under LGPL - original licence link has changed is not relivant.
51840  *
51841  * Fork - LGPL
51842  * <script type="text/javascript">
51843  */
51844  
51845 /**
51846  * @class Roo.LayoutRegion
51847  * @extends Roo.BasicLayoutRegion
51848  * This class represents a region in a layout manager.
51849  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
51850  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
51851  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
51852  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
51853  * @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})
51854  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
51855  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
51856  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
51857  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
51858  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
51859  * @cfg {String}    title           The title for the region (overrides panel titles)
51860  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
51861  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
51862  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
51863  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
51864  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
51865  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
51866  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
51867  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
51868  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
51869  * @cfg {Boolean}   showPin         True to show a pin button
51870  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
51871  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
51872  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
51873  * @cfg {Number}    width           For East/West panels
51874  * @cfg {Number}    height          For North/South panels
51875  * @cfg {Boolean}   split           To show the splitter
51876  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
51877  */
51878 Roo.LayoutRegion = function(mgr, config, pos){
51879     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
51880     var dh = Roo.DomHelper;
51881     /** This region's container element 
51882     * @type Roo.Element */
51883     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
51884     /** This region's title element 
51885     * @type Roo.Element */
51886
51887     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
51888         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
51889         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
51890     ]}, true);
51891     this.titleEl.enableDisplayMode();
51892     /** This region's title text element 
51893     * @type HTMLElement */
51894     this.titleTextEl = this.titleEl.dom.firstChild;
51895     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
51896     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
51897     this.closeBtn.enableDisplayMode();
51898     this.closeBtn.on("click", this.closeClicked, this);
51899     this.closeBtn.hide();
51900
51901     this.createBody(config);
51902     this.visible = true;
51903     this.collapsed = false;
51904
51905     if(config.hideWhenEmpty){
51906         this.hide();
51907         this.on("paneladded", this.validateVisibility, this);
51908         this.on("panelremoved", this.validateVisibility, this);
51909     }
51910     this.applyConfig(config);
51911 };
51912
51913 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
51914
51915     createBody : function(){
51916         /** This region's body element 
51917         * @type Roo.Element */
51918         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
51919     },
51920
51921     applyConfig : function(c){
51922         if(c.collapsible && this.position != "center" && !this.collapsedEl){
51923             var dh = Roo.DomHelper;
51924             if(c.titlebar !== false){
51925                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
51926                 this.collapseBtn.on("click", this.collapse, this);
51927                 this.collapseBtn.enableDisplayMode();
51928
51929                 if(c.showPin === true || this.showPin){
51930                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
51931                     this.stickBtn.enableDisplayMode();
51932                     this.stickBtn.on("click", this.expand, this);
51933                     this.stickBtn.hide();
51934                 }
51935             }
51936             /** This region's collapsed element
51937             * @type Roo.Element */
51938             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
51939                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
51940             ]}, true);
51941             if(c.floatable !== false){
51942                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
51943                this.collapsedEl.on("click", this.collapseClick, this);
51944             }
51945
51946             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
51947                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
51948                    id: "message", unselectable: "on", style:{"float":"left"}});
51949                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
51950              }
51951             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
51952             this.expandBtn.on("click", this.expand, this);
51953         }
51954         if(this.collapseBtn){
51955             this.collapseBtn.setVisible(c.collapsible == true);
51956         }
51957         this.cmargins = c.cmargins || this.cmargins ||
51958                          (this.position == "west" || this.position == "east" ?
51959                              {top: 0, left: 2, right:2, bottom: 0} :
51960                              {top: 2, left: 0, right:0, bottom: 2});
51961         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
51962         this.bottomTabs = c.tabPosition != "top";
51963         this.autoScroll = c.autoScroll || false;
51964         if(this.autoScroll){
51965             this.bodyEl.setStyle("overflow", "auto");
51966         }else{
51967             this.bodyEl.setStyle("overflow", "hidden");
51968         }
51969         //if(c.titlebar !== false){
51970             if((!c.titlebar && !c.title) || c.titlebar === false){
51971                 this.titleEl.hide();
51972             }else{
51973                 this.titleEl.show();
51974                 if(c.title){
51975                     this.titleTextEl.innerHTML = c.title;
51976                 }
51977             }
51978         //}
51979         this.duration = c.duration || .30;
51980         this.slideDuration = c.slideDuration || .45;
51981         this.config = c;
51982         if(c.collapsed){
51983             this.collapse(true);
51984         }
51985         if(c.hidden){
51986             this.hide();
51987         }
51988     },
51989     /**
51990      * Returns true if this region is currently visible.
51991      * @return {Boolean}
51992      */
51993     isVisible : function(){
51994         return this.visible;
51995     },
51996
51997     /**
51998      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
51999      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
52000      */
52001     setCollapsedTitle : function(title){
52002         title = title || "&#160;";
52003         if(this.collapsedTitleTextEl){
52004             this.collapsedTitleTextEl.innerHTML = title;
52005         }
52006     },
52007
52008     getBox : function(){
52009         var b;
52010         if(!this.collapsed){
52011             b = this.el.getBox(false, true);
52012         }else{
52013             b = this.collapsedEl.getBox(false, true);
52014         }
52015         return b;
52016     },
52017
52018     getMargins : function(){
52019         return this.collapsed ? this.cmargins : this.margins;
52020     },
52021
52022     highlight : function(){
52023         this.el.addClass("x-layout-panel-dragover");
52024     },
52025
52026     unhighlight : function(){
52027         this.el.removeClass("x-layout-panel-dragover");
52028     },
52029
52030     updateBox : function(box){
52031         this.box = box;
52032         if(!this.collapsed){
52033             this.el.dom.style.left = box.x + "px";
52034             this.el.dom.style.top = box.y + "px";
52035             this.updateBody(box.width, box.height);
52036         }else{
52037             this.collapsedEl.dom.style.left = box.x + "px";
52038             this.collapsedEl.dom.style.top = box.y + "px";
52039             this.collapsedEl.setSize(box.width, box.height);
52040         }
52041         if(this.tabs){
52042             this.tabs.autoSizeTabs();
52043         }
52044     },
52045
52046     updateBody : function(w, h){
52047         if(w !== null){
52048             this.el.setWidth(w);
52049             w -= this.el.getBorderWidth("rl");
52050             if(this.config.adjustments){
52051                 w += this.config.adjustments[0];
52052             }
52053         }
52054         if(h !== null){
52055             this.el.setHeight(h);
52056             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52057             h -= this.el.getBorderWidth("tb");
52058             if(this.config.adjustments){
52059                 h += this.config.adjustments[1];
52060             }
52061             this.bodyEl.setHeight(h);
52062             if(this.tabs){
52063                 h = this.tabs.syncHeight(h);
52064             }
52065         }
52066         if(this.panelSize){
52067             w = w !== null ? w : this.panelSize.width;
52068             h = h !== null ? h : this.panelSize.height;
52069         }
52070         if(this.activePanel){
52071             var el = this.activePanel.getEl();
52072             w = w !== null ? w : el.getWidth();
52073             h = h !== null ? h : el.getHeight();
52074             this.panelSize = {width: w, height: h};
52075             this.activePanel.setSize(w, h);
52076         }
52077         if(Roo.isIE && this.tabs){
52078             this.tabs.el.repaint();
52079         }
52080     },
52081
52082     /**
52083      * Returns the container element for this region.
52084      * @return {Roo.Element}
52085      */
52086     getEl : function(){
52087         return this.el;
52088     },
52089
52090     /**
52091      * Hides this region.
52092      */
52093     hide : function(){
52094         if(!this.collapsed){
52095             this.el.dom.style.left = "-2000px";
52096             this.el.hide();
52097         }else{
52098             this.collapsedEl.dom.style.left = "-2000px";
52099             this.collapsedEl.hide();
52100         }
52101         this.visible = false;
52102         this.fireEvent("visibilitychange", this, false);
52103     },
52104
52105     /**
52106      * Shows this region if it was previously hidden.
52107      */
52108     show : function(){
52109         if(!this.collapsed){
52110             this.el.show();
52111         }else{
52112             this.collapsedEl.show();
52113         }
52114         this.visible = true;
52115         this.fireEvent("visibilitychange", this, true);
52116     },
52117
52118     closeClicked : function(){
52119         if(this.activePanel){
52120             this.remove(this.activePanel);
52121         }
52122     },
52123
52124     collapseClick : function(e){
52125         if(this.isSlid){
52126            e.stopPropagation();
52127            this.slideIn();
52128         }else{
52129            e.stopPropagation();
52130            this.slideOut();
52131         }
52132     },
52133
52134     /**
52135      * Collapses this region.
52136      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52137      */
52138     collapse : function(skipAnim, skipCheck = false){
52139         if(this.collapsed) {
52140             return;
52141         }
52142         
52143         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52144             
52145             this.collapsed = true;
52146             if(this.split){
52147                 this.split.el.hide();
52148             }
52149             if(this.config.animate && skipAnim !== true){
52150                 this.fireEvent("invalidated", this);
52151                 this.animateCollapse();
52152             }else{
52153                 this.el.setLocation(-20000,-20000);
52154                 this.el.hide();
52155                 this.collapsedEl.show();
52156                 this.fireEvent("collapsed", this);
52157                 this.fireEvent("invalidated", this);
52158             }
52159         }
52160         
52161     },
52162
52163     animateCollapse : function(){
52164         // overridden
52165     },
52166
52167     /**
52168      * Expands this region if it was previously collapsed.
52169      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
52170      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
52171      */
52172     expand : function(e, skipAnim){
52173         if(e) {
52174             e.stopPropagation();
52175         }
52176         if(!this.collapsed || this.el.hasActiveFx()) {
52177             return;
52178         }
52179         if(this.isSlid){
52180             this.afterSlideIn();
52181             skipAnim = true;
52182         }
52183         this.collapsed = false;
52184         if(this.config.animate && skipAnim !== true){
52185             this.animateExpand();
52186         }else{
52187             this.el.show();
52188             if(this.split){
52189                 this.split.el.show();
52190             }
52191             this.collapsedEl.setLocation(-2000,-2000);
52192             this.collapsedEl.hide();
52193             this.fireEvent("invalidated", this);
52194             this.fireEvent("expanded", this);
52195         }
52196     },
52197
52198     animateExpand : function(){
52199         // overridden
52200     },
52201
52202     initTabs : function()
52203     {
52204         this.bodyEl.setStyle("overflow", "hidden");
52205         var ts = new Roo.TabPanel(
52206                 this.bodyEl.dom,
52207                 {
52208                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
52209                     disableTooltips: this.config.disableTabTips,
52210                     toolbar : this.config.toolbar
52211                 }
52212         );
52213         if(this.config.hideTabs){
52214             ts.stripWrap.setDisplayed(false);
52215         }
52216         this.tabs = ts;
52217         ts.resizeTabs = this.config.resizeTabs === true;
52218         ts.minTabWidth = this.config.minTabWidth || 40;
52219         ts.maxTabWidth = this.config.maxTabWidth || 250;
52220         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
52221         ts.monitorResize = false;
52222         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52223         ts.bodyEl.addClass('x-layout-tabs-body');
52224         this.panels.each(this.initPanelAsTab, this);
52225     },
52226
52227     initPanelAsTab : function(panel){
52228         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
52229                     this.config.closeOnTab && panel.isClosable());
52230         if(panel.tabTip !== undefined){
52231             ti.setTooltip(panel.tabTip);
52232         }
52233         ti.on("activate", function(){
52234               this.setActivePanel(panel);
52235         }, this);
52236         if(this.config.closeOnTab){
52237             ti.on("beforeclose", function(t, e){
52238                 e.cancel = true;
52239                 this.remove(panel);
52240             }, this);
52241         }
52242         return ti;
52243     },
52244
52245     updatePanelTitle : function(panel, title){
52246         if(this.activePanel == panel){
52247             this.updateTitle(title);
52248         }
52249         if(this.tabs){
52250             var ti = this.tabs.getTab(panel.getEl().id);
52251             ti.setText(title);
52252             if(panel.tabTip !== undefined){
52253                 ti.setTooltip(panel.tabTip);
52254             }
52255         }
52256     },
52257
52258     updateTitle : function(title){
52259         if(this.titleTextEl && !this.config.title){
52260             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
52261         }
52262     },
52263
52264     setActivePanel : function(panel){
52265         panel = this.getPanel(panel);
52266         if(this.activePanel && this.activePanel != panel){
52267             this.activePanel.setActiveState(false);
52268         }
52269         this.activePanel = panel;
52270         panel.setActiveState(true);
52271         if(this.panelSize){
52272             panel.setSize(this.panelSize.width, this.panelSize.height);
52273         }
52274         if(this.closeBtn){
52275             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
52276         }
52277         this.updateTitle(panel.getTitle());
52278         if(this.tabs){
52279             this.fireEvent("invalidated", this);
52280         }
52281         this.fireEvent("panelactivated", this, panel);
52282     },
52283
52284     /**
52285      * Shows the specified panel.
52286      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
52287      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
52288      */
52289     showPanel : function(panel)
52290     {
52291         panel = this.getPanel(panel);
52292         if(panel){
52293             if(this.tabs){
52294                 var tab = this.tabs.getTab(panel.getEl().id);
52295                 if(tab.isHidden()){
52296                     this.tabs.unhideTab(tab.id);
52297                 }
52298                 tab.activate();
52299             }else{
52300                 this.setActivePanel(panel);
52301             }
52302         }
52303         return panel;
52304     },
52305
52306     /**
52307      * Get the active panel for this region.
52308      * @return {Roo.ContentPanel} The active panel or null
52309      */
52310     getActivePanel : function(){
52311         return this.activePanel;
52312     },
52313
52314     validateVisibility : function(){
52315         if(this.panels.getCount() < 1){
52316             this.updateTitle("&#160;");
52317             this.closeBtn.hide();
52318             this.hide();
52319         }else{
52320             if(!this.isVisible()){
52321                 this.show();
52322             }
52323         }
52324     },
52325
52326     /**
52327      * Adds the passed ContentPanel(s) to this region.
52328      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52329      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
52330      */
52331     add : function(panel){
52332         if(arguments.length > 1){
52333             for(var i = 0, len = arguments.length; i < len; i++) {
52334                 this.add(arguments[i]);
52335             }
52336             return null;
52337         }
52338         if(this.hasPanel(panel)){
52339             this.showPanel(panel);
52340             return panel;
52341         }
52342         panel.setRegion(this);
52343         this.panels.add(panel);
52344         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
52345             this.bodyEl.dom.appendChild(panel.getEl().dom);
52346             if(panel.background !== true){
52347                 this.setActivePanel(panel);
52348             }
52349             this.fireEvent("paneladded", this, panel);
52350             return panel;
52351         }
52352         if(!this.tabs){
52353             this.initTabs();
52354         }else{
52355             this.initPanelAsTab(panel);
52356         }
52357         if(panel.background !== true){
52358             this.tabs.activate(panel.getEl().id);
52359         }
52360         this.fireEvent("paneladded", this, panel);
52361         return panel;
52362     },
52363
52364     /**
52365      * Hides the tab for the specified panel.
52366      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52367      */
52368     hidePanel : function(panel){
52369         if(this.tabs && (panel = this.getPanel(panel))){
52370             this.tabs.hideTab(panel.getEl().id);
52371         }
52372     },
52373
52374     /**
52375      * Unhides the tab for a previously hidden panel.
52376      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52377      */
52378     unhidePanel : function(panel){
52379         if(this.tabs && (panel = this.getPanel(panel))){
52380             this.tabs.unhideTab(panel.getEl().id);
52381         }
52382     },
52383
52384     clearPanels : function(){
52385         while(this.panels.getCount() > 0){
52386              this.remove(this.panels.first());
52387         }
52388     },
52389
52390     /**
52391      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52392      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
52393      * @param {Boolean} preservePanel Overrides the config preservePanel option
52394      * @return {Roo.ContentPanel} The panel that was removed
52395      */
52396     remove : function(panel, preservePanel){
52397         panel = this.getPanel(panel);
52398         if(!panel){
52399             return null;
52400         }
52401         var e = {};
52402         this.fireEvent("beforeremove", this, panel, e);
52403         if(e.cancel === true){
52404             return null;
52405         }
52406         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
52407         var panelId = panel.getId();
52408         this.panels.removeKey(panelId);
52409         if(preservePanel){
52410             document.body.appendChild(panel.getEl().dom);
52411         }
52412         if(this.tabs){
52413             this.tabs.removeTab(panel.getEl().id);
52414         }else if (!preservePanel){
52415             this.bodyEl.dom.removeChild(panel.getEl().dom);
52416         }
52417         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
52418             var p = this.panels.first();
52419             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
52420             tempEl.appendChild(p.getEl().dom);
52421             this.bodyEl.update("");
52422             this.bodyEl.dom.appendChild(p.getEl().dom);
52423             tempEl = null;
52424             this.updateTitle(p.getTitle());
52425             this.tabs = null;
52426             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
52427             this.setActivePanel(p);
52428         }
52429         panel.setRegion(null);
52430         if(this.activePanel == panel){
52431             this.activePanel = null;
52432         }
52433         if(this.config.autoDestroy !== false && preservePanel !== true){
52434             try{panel.destroy();}catch(e){}
52435         }
52436         this.fireEvent("panelremoved", this, panel);
52437         return panel;
52438     },
52439
52440     /**
52441      * Returns the TabPanel component used by this region
52442      * @return {Roo.TabPanel}
52443      */
52444     getTabs : function(){
52445         return this.tabs;
52446     },
52447
52448     createTool : function(parentEl, className){
52449         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
52450             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
52451         btn.addClassOnOver("x-layout-tools-button-over");
52452         return btn;
52453     }
52454 });/*
52455  * Based on:
52456  * Ext JS Library 1.1.1
52457  * Copyright(c) 2006-2007, Ext JS, LLC.
52458  *
52459  * Originally Released Under LGPL - original licence link has changed is not relivant.
52460  *
52461  * Fork - LGPL
52462  * <script type="text/javascript">
52463  */
52464  
52465
52466
52467 /**
52468  * @class Roo.SplitLayoutRegion
52469  * @extends Roo.LayoutRegion
52470  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
52471  */
52472 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
52473     this.cursor = cursor;
52474     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
52475 };
52476
52477 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
52478     splitTip : "Drag to resize.",
52479     collapsibleSplitTip : "Drag to resize. Double click to hide.",
52480     useSplitTips : false,
52481
52482     applyConfig : function(config){
52483         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
52484         if(config.split){
52485             if(!this.split){
52486                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
52487                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
52488                 /** The SplitBar for this region 
52489                 * @type Roo.SplitBar */
52490                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
52491                 this.split.on("moved", this.onSplitMove, this);
52492                 this.split.useShim = config.useShim === true;
52493                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
52494                 if(this.useSplitTips){
52495                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
52496                 }
52497                 if(config.collapsible){
52498                     this.split.el.on("dblclick", this.collapse,  this);
52499                 }
52500             }
52501             if(typeof config.minSize != "undefined"){
52502                 this.split.minSize = config.minSize;
52503             }
52504             if(typeof config.maxSize != "undefined"){
52505                 this.split.maxSize = config.maxSize;
52506             }
52507             if(config.hideWhenEmpty || config.hidden || config.collapsed){
52508                 this.hideSplitter();
52509             }
52510         }
52511     },
52512
52513     getHMaxSize : function(){
52514          var cmax = this.config.maxSize || 10000;
52515          var center = this.mgr.getRegion("center");
52516          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
52517     },
52518
52519     getVMaxSize : function(){
52520          var cmax = this.config.maxSize || 10000;
52521          var center = this.mgr.getRegion("center");
52522          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
52523     },
52524
52525     onSplitMove : function(split, newSize){
52526         this.fireEvent("resized", this, newSize);
52527     },
52528     
52529     /** 
52530      * Returns the {@link Roo.SplitBar} for this region.
52531      * @return {Roo.SplitBar}
52532      */
52533     getSplitBar : function(){
52534         return this.split;
52535     },
52536     
52537     hide : function(){
52538         this.hideSplitter();
52539         Roo.SplitLayoutRegion.superclass.hide.call(this);
52540     },
52541
52542     hideSplitter : function(){
52543         if(this.split){
52544             this.split.el.setLocation(-2000,-2000);
52545             this.split.el.hide();
52546         }
52547     },
52548
52549     show : function(){
52550         if(this.split){
52551             this.split.el.show();
52552         }
52553         Roo.SplitLayoutRegion.superclass.show.call(this);
52554     },
52555     
52556     beforeSlide: function(){
52557         if(Roo.isGecko){// firefox overflow auto bug workaround
52558             this.bodyEl.clip();
52559             if(this.tabs) {
52560                 this.tabs.bodyEl.clip();
52561             }
52562             if(this.activePanel){
52563                 this.activePanel.getEl().clip();
52564                 
52565                 if(this.activePanel.beforeSlide){
52566                     this.activePanel.beforeSlide();
52567                 }
52568             }
52569         }
52570     },
52571     
52572     afterSlide : function(){
52573         if(Roo.isGecko){// firefox overflow auto bug workaround
52574             this.bodyEl.unclip();
52575             if(this.tabs) {
52576                 this.tabs.bodyEl.unclip();
52577             }
52578             if(this.activePanel){
52579                 this.activePanel.getEl().unclip();
52580                 if(this.activePanel.afterSlide){
52581                     this.activePanel.afterSlide();
52582                 }
52583             }
52584         }
52585     },
52586
52587     initAutoHide : function(){
52588         if(this.autoHide !== false){
52589             if(!this.autoHideHd){
52590                 var st = new Roo.util.DelayedTask(this.slideIn, this);
52591                 this.autoHideHd = {
52592                     "mouseout": function(e){
52593                         if(!e.within(this.el, true)){
52594                             st.delay(500);
52595                         }
52596                     },
52597                     "mouseover" : function(e){
52598                         st.cancel();
52599                     },
52600                     scope : this
52601                 };
52602             }
52603             this.el.on(this.autoHideHd);
52604         }
52605     },
52606
52607     clearAutoHide : function(){
52608         if(this.autoHide !== false){
52609             this.el.un("mouseout", this.autoHideHd.mouseout);
52610             this.el.un("mouseover", this.autoHideHd.mouseover);
52611         }
52612     },
52613
52614     clearMonitor : function(){
52615         Roo.get(document).un("click", this.slideInIf, this);
52616     },
52617
52618     // these names are backwards but not changed for compat
52619     slideOut : function(){
52620         if(this.isSlid || this.el.hasActiveFx()){
52621             return;
52622         }
52623         this.isSlid = true;
52624         if(this.collapseBtn){
52625             this.collapseBtn.hide();
52626         }
52627         this.closeBtnState = this.closeBtn.getStyle('display');
52628         this.closeBtn.hide();
52629         if(this.stickBtn){
52630             this.stickBtn.show();
52631         }
52632         this.el.show();
52633         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
52634         this.beforeSlide();
52635         this.el.setStyle("z-index", 10001);
52636         this.el.slideIn(this.getSlideAnchor(), {
52637             callback: function(){
52638                 this.afterSlide();
52639                 this.initAutoHide();
52640                 Roo.get(document).on("click", this.slideInIf, this);
52641                 this.fireEvent("slideshow", this);
52642             },
52643             scope: this,
52644             block: true
52645         });
52646     },
52647
52648     afterSlideIn : function(){
52649         this.clearAutoHide();
52650         this.isSlid = false;
52651         this.clearMonitor();
52652         this.el.setStyle("z-index", "");
52653         if(this.collapseBtn){
52654             this.collapseBtn.show();
52655         }
52656         this.closeBtn.setStyle('display', this.closeBtnState);
52657         if(this.stickBtn){
52658             this.stickBtn.hide();
52659         }
52660         this.fireEvent("slidehide", this);
52661     },
52662
52663     slideIn : function(cb){
52664         if(!this.isSlid || this.el.hasActiveFx()){
52665             Roo.callback(cb);
52666             return;
52667         }
52668         this.isSlid = false;
52669         this.beforeSlide();
52670         this.el.slideOut(this.getSlideAnchor(), {
52671             callback: function(){
52672                 this.el.setLeftTop(-10000, -10000);
52673                 this.afterSlide();
52674                 this.afterSlideIn();
52675                 Roo.callback(cb);
52676             },
52677             scope: this,
52678             block: true
52679         });
52680     },
52681     
52682     slideInIf : function(e){
52683         if(!e.within(this.el)){
52684             this.slideIn();
52685         }
52686     },
52687
52688     animateCollapse : function(){
52689         this.beforeSlide();
52690         this.el.setStyle("z-index", 20000);
52691         var anchor = this.getSlideAnchor();
52692         this.el.slideOut(anchor, {
52693             callback : function(){
52694                 this.el.setStyle("z-index", "");
52695                 this.collapsedEl.slideIn(anchor, {duration:.3});
52696                 this.afterSlide();
52697                 this.el.setLocation(-10000,-10000);
52698                 this.el.hide();
52699                 this.fireEvent("collapsed", this);
52700             },
52701             scope: this,
52702             block: true
52703         });
52704     },
52705
52706     animateExpand : function(){
52707         this.beforeSlide();
52708         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
52709         this.el.setStyle("z-index", 20000);
52710         this.collapsedEl.hide({
52711             duration:.1
52712         });
52713         this.el.slideIn(this.getSlideAnchor(), {
52714             callback : function(){
52715                 this.el.setStyle("z-index", "");
52716                 this.afterSlide();
52717                 if(this.split){
52718                     this.split.el.show();
52719                 }
52720                 this.fireEvent("invalidated", this);
52721                 this.fireEvent("expanded", this);
52722             },
52723             scope: this,
52724             block: true
52725         });
52726     },
52727
52728     anchors : {
52729         "west" : "left",
52730         "east" : "right",
52731         "north" : "top",
52732         "south" : "bottom"
52733     },
52734
52735     sanchors : {
52736         "west" : "l",
52737         "east" : "r",
52738         "north" : "t",
52739         "south" : "b"
52740     },
52741
52742     canchors : {
52743         "west" : "tl-tr",
52744         "east" : "tr-tl",
52745         "north" : "tl-bl",
52746         "south" : "bl-tl"
52747     },
52748
52749     getAnchor : function(){
52750         return this.anchors[this.position];
52751     },
52752
52753     getCollapseAnchor : function(){
52754         return this.canchors[this.position];
52755     },
52756
52757     getSlideAnchor : function(){
52758         return this.sanchors[this.position];
52759     },
52760
52761     getAlignAdj : function(){
52762         var cm = this.cmargins;
52763         switch(this.position){
52764             case "west":
52765                 return [0, 0];
52766             break;
52767             case "east":
52768                 return [0, 0];
52769             break;
52770             case "north":
52771                 return [0, 0];
52772             break;
52773             case "south":
52774                 return [0, 0];
52775             break;
52776         }
52777     },
52778
52779     getExpandAdj : function(){
52780         var c = this.collapsedEl, cm = this.cmargins;
52781         switch(this.position){
52782             case "west":
52783                 return [-(cm.right+c.getWidth()+cm.left), 0];
52784             break;
52785             case "east":
52786                 return [cm.right+c.getWidth()+cm.left, 0];
52787             break;
52788             case "north":
52789                 return [0, -(cm.top+cm.bottom+c.getHeight())];
52790             break;
52791             case "south":
52792                 return [0, cm.top+cm.bottom+c.getHeight()];
52793             break;
52794         }
52795     }
52796 });/*
52797  * Based on:
52798  * Ext JS Library 1.1.1
52799  * Copyright(c) 2006-2007, Ext JS, LLC.
52800  *
52801  * Originally Released Under LGPL - original licence link has changed is not relivant.
52802  *
52803  * Fork - LGPL
52804  * <script type="text/javascript">
52805  */
52806 /*
52807  * These classes are private internal classes
52808  */
52809 Roo.CenterLayoutRegion = function(mgr, config){
52810     Roo.LayoutRegion.call(this, mgr, config, "center");
52811     this.visible = true;
52812     this.minWidth = config.minWidth || 20;
52813     this.minHeight = config.minHeight || 20;
52814 };
52815
52816 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
52817     hide : function(){
52818         // center panel can't be hidden
52819     },
52820     
52821     show : function(){
52822         // center panel can't be hidden
52823     },
52824     
52825     getMinWidth: function(){
52826         return this.minWidth;
52827     },
52828     
52829     getMinHeight: function(){
52830         return this.minHeight;
52831     }
52832 });
52833
52834
52835 Roo.NorthLayoutRegion = function(mgr, config){
52836     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
52837     if(this.split){
52838         this.split.placement = Roo.SplitBar.TOP;
52839         this.split.orientation = Roo.SplitBar.VERTICAL;
52840         this.split.el.addClass("x-layout-split-v");
52841     }
52842     var size = config.initialSize || config.height;
52843     if(typeof size != "undefined"){
52844         this.el.setHeight(size);
52845     }
52846 };
52847 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
52848     orientation: Roo.SplitBar.VERTICAL,
52849     getBox : function(){
52850         if(this.collapsed){
52851             return this.collapsedEl.getBox();
52852         }
52853         var box = this.el.getBox();
52854         if(this.split){
52855             box.height += this.split.el.getHeight();
52856         }
52857         return box;
52858     },
52859     
52860     updateBox : function(box){
52861         if(this.split && !this.collapsed){
52862             box.height -= this.split.el.getHeight();
52863             this.split.el.setLeft(box.x);
52864             this.split.el.setTop(box.y+box.height);
52865             this.split.el.setWidth(box.width);
52866         }
52867         if(this.collapsed){
52868             this.updateBody(box.width, null);
52869         }
52870         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52871     }
52872 });
52873
52874 Roo.SouthLayoutRegion = function(mgr, config){
52875     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
52876     if(this.split){
52877         this.split.placement = Roo.SplitBar.BOTTOM;
52878         this.split.orientation = Roo.SplitBar.VERTICAL;
52879         this.split.el.addClass("x-layout-split-v");
52880     }
52881     var size = config.initialSize || config.height;
52882     if(typeof size != "undefined"){
52883         this.el.setHeight(size);
52884     }
52885 };
52886 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
52887     orientation: Roo.SplitBar.VERTICAL,
52888     getBox : function(){
52889         if(this.collapsed){
52890             return this.collapsedEl.getBox();
52891         }
52892         var box = this.el.getBox();
52893         if(this.split){
52894             var sh = this.split.el.getHeight();
52895             box.height += sh;
52896             box.y -= sh;
52897         }
52898         return box;
52899     },
52900     
52901     updateBox : function(box){
52902         if(this.split && !this.collapsed){
52903             var sh = this.split.el.getHeight();
52904             box.height -= sh;
52905             box.y += sh;
52906             this.split.el.setLeft(box.x);
52907             this.split.el.setTop(box.y-sh);
52908             this.split.el.setWidth(box.width);
52909         }
52910         if(this.collapsed){
52911             this.updateBody(box.width, null);
52912         }
52913         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52914     }
52915 });
52916
52917 Roo.EastLayoutRegion = function(mgr, config){
52918     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
52919     if(this.split){
52920         this.split.placement = Roo.SplitBar.RIGHT;
52921         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52922         this.split.el.addClass("x-layout-split-h");
52923     }
52924     var size = config.initialSize || config.width;
52925     if(typeof size != "undefined"){
52926         this.el.setWidth(size);
52927     }
52928 };
52929 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
52930     orientation: Roo.SplitBar.HORIZONTAL,
52931     getBox : function(){
52932         if(this.collapsed){
52933             return this.collapsedEl.getBox();
52934         }
52935         var box = this.el.getBox();
52936         if(this.split){
52937             var sw = this.split.el.getWidth();
52938             box.width += sw;
52939             box.x -= sw;
52940         }
52941         return box;
52942     },
52943
52944     updateBox : function(box){
52945         if(this.split && !this.collapsed){
52946             var sw = this.split.el.getWidth();
52947             box.width -= sw;
52948             this.split.el.setLeft(box.x);
52949             this.split.el.setTop(box.y);
52950             this.split.el.setHeight(box.height);
52951             box.x += sw;
52952         }
52953         if(this.collapsed){
52954             this.updateBody(null, box.height);
52955         }
52956         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52957     }
52958 });
52959
52960 Roo.WestLayoutRegion = function(mgr, config){
52961     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
52962     if(this.split){
52963         this.split.placement = Roo.SplitBar.LEFT;
52964         this.split.orientation = Roo.SplitBar.HORIZONTAL;
52965         this.split.el.addClass("x-layout-split-h");
52966     }
52967     var size = config.initialSize || config.width;
52968     if(typeof size != "undefined"){
52969         this.el.setWidth(size);
52970     }
52971 };
52972 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
52973     orientation: Roo.SplitBar.HORIZONTAL,
52974     getBox : function(){
52975         if(this.collapsed){
52976             return this.collapsedEl.getBox();
52977         }
52978         var box = this.el.getBox();
52979         if(this.split){
52980             box.width += this.split.el.getWidth();
52981         }
52982         return box;
52983     },
52984     
52985     updateBox : function(box){
52986         if(this.split && !this.collapsed){
52987             var sw = this.split.el.getWidth();
52988             box.width -= sw;
52989             this.split.el.setLeft(box.x+box.width);
52990             this.split.el.setTop(box.y);
52991             this.split.el.setHeight(box.height);
52992         }
52993         if(this.collapsed){
52994             this.updateBody(null, box.height);
52995         }
52996         Roo.LayoutRegion.prototype.updateBox.call(this, box);
52997     }
52998 });
52999 /*
53000  * Based on:
53001  * Ext JS Library 1.1.1
53002  * Copyright(c) 2006-2007, Ext JS, LLC.
53003  *
53004  * Originally Released Under LGPL - original licence link has changed is not relivant.
53005  *
53006  * Fork - LGPL
53007  * <script type="text/javascript">
53008  */
53009  
53010  
53011 /*
53012  * Private internal class for reading and applying state
53013  */
53014 Roo.LayoutStateManager = function(layout){
53015      // default empty state
53016      this.state = {
53017         north: {},
53018         south: {},
53019         east: {},
53020         west: {}       
53021     };
53022 };
53023
53024 Roo.LayoutStateManager.prototype = {
53025     init : function(layout, provider){
53026         this.provider = provider;
53027         var state = provider.get(layout.id+"-layout-state");
53028         if(state){
53029             var wasUpdating = layout.isUpdating();
53030             if(!wasUpdating){
53031                 layout.beginUpdate();
53032             }
53033             for(var key in state){
53034                 if(typeof state[key] != "function"){
53035                     var rstate = state[key];
53036                     var r = layout.getRegion(key);
53037                     if(r && rstate){
53038                         if(rstate.size){
53039                             r.resizeTo(rstate.size);
53040                         }
53041                         if(rstate.collapsed == true){
53042                             r.collapse(true);
53043                         }else{
53044                             r.expand(null, true);
53045                         }
53046                     }
53047                 }
53048             }
53049             if(!wasUpdating){
53050                 layout.endUpdate();
53051             }
53052             this.state = state; 
53053         }
53054         this.layout = layout;
53055         layout.on("regionresized", this.onRegionResized, this);
53056         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53057         layout.on("regionexpanded", this.onRegionExpanded, this);
53058     },
53059     
53060     storeState : function(){
53061         this.provider.set(this.layout.id+"-layout-state", this.state);
53062     },
53063     
53064     onRegionResized : function(region, newSize){
53065         this.state[region.getPosition()].size = newSize;
53066         this.storeState();
53067     },
53068     
53069     onRegionCollapsed : function(region){
53070         this.state[region.getPosition()].collapsed = true;
53071         this.storeState();
53072     },
53073     
53074     onRegionExpanded : function(region){
53075         this.state[region.getPosition()].collapsed = false;
53076         this.storeState();
53077     }
53078 };/*
53079  * Based on:
53080  * Ext JS Library 1.1.1
53081  * Copyright(c) 2006-2007, Ext JS, LLC.
53082  *
53083  * Originally Released Under LGPL - original licence link has changed is not relivant.
53084  *
53085  * Fork - LGPL
53086  * <script type="text/javascript">
53087  */
53088 /**
53089  * @class Roo.ContentPanel
53090  * @extends Roo.util.Observable
53091  * A basic ContentPanel element.
53092  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53093  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53094  * @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
53095  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53096  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53097  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53098  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53099  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53100  * @cfg {String} title          The title for this panel
53101  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53102  * @cfg {String} url            Calls {@link #setUrl} with this value
53103  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53104  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53105  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53106  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53107
53108  * @constructor
53109  * Create a new ContentPanel.
53110  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53111  * @param {String/Object} config A string to set only the title or a config object
53112  * @param {String} content (optional) Set the HTML content for this panel
53113  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53114  */
53115 Roo.ContentPanel = function(el, config, content){
53116     
53117      
53118     /*
53119     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53120         config = el;
53121         el = Roo.id();
53122     }
53123     if (config && config.parentLayout) { 
53124         el = config.parentLayout.el.createChild(); 
53125     }
53126     */
53127     if(el.autoCreate){ // xtype is available if this is called from factory
53128         config = el;
53129         el = Roo.id();
53130     }
53131     this.el = Roo.get(el);
53132     if(!this.el && config && config.autoCreate){
53133         if(typeof config.autoCreate == "object"){
53134             if(!config.autoCreate.id){
53135                 config.autoCreate.id = config.id||el;
53136             }
53137             this.el = Roo.DomHelper.append(document.body,
53138                         config.autoCreate, true);
53139         }else{
53140             this.el = Roo.DomHelper.append(document.body,
53141                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53142         }
53143     }
53144     this.closable = false;
53145     this.loaded = false;
53146     this.active = false;
53147     if(typeof config == "string"){
53148         this.title = config;
53149     }else{
53150         Roo.apply(this, config);
53151     }
53152     
53153     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
53154         this.wrapEl = this.el.wrap();
53155         this.toolbar.container = this.el.insertSibling(false, 'before');
53156         this.toolbar = new Roo.Toolbar(this.toolbar);
53157     }
53158     
53159     // xtype created footer. - not sure if will work as we normally have to render first..
53160     if (this.footer && !this.footer.el && this.footer.xtype) {
53161         if (!this.wrapEl) {
53162             this.wrapEl = this.el.wrap();
53163         }
53164     
53165         this.footer.container = this.wrapEl.createChild();
53166          
53167         this.footer = Roo.factory(this.footer, Roo);
53168         
53169     }
53170     
53171     if(this.resizeEl){
53172         this.resizeEl = Roo.get(this.resizeEl, true);
53173     }else{
53174         this.resizeEl = this.el;
53175     }
53176     // handle view.xtype
53177     
53178  
53179     
53180     
53181     this.addEvents({
53182         /**
53183          * @event activate
53184          * Fires when this panel is activated. 
53185          * @param {Roo.ContentPanel} this
53186          */
53187         "activate" : true,
53188         /**
53189          * @event deactivate
53190          * Fires when this panel is activated. 
53191          * @param {Roo.ContentPanel} this
53192          */
53193         "deactivate" : true,
53194
53195         /**
53196          * @event resize
53197          * Fires when this panel is resized if fitToFrame is true.
53198          * @param {Roo.ContentPanel} this
53199          * @param {Number} width The width after any component adjustments
53200          * @param {Number} height The height after any component adjustments
53201          */
53202         "resize" : true,
53203         
53204          /**
53205          * @event render
53206          * Fires when this tab is created
53207          * @param {Roo.ContentPanel} this
53208          */
53209         "render" : true
53210         
53211         
53212         
53213     });
53214     
53215
53216     
53217     
53218     if(this.autoScroll){
53219         this.resizeEl.setStyle("overflow", "auto");
53220     } else {
53221         // fix randome scrolling
53222         this.el.on('scroll', function() {
53223             Roo.log('fix random scolling');
53224             this.scrollTo('top',0); 
53225         });
53226     }
53227     content = content || this.content;
53228     if(content){
53229         this.setContent(content);
53230     }
53231     if(config && config.url){
53232         this.setUrl(this.url, this.params, this.loadOnce);
53233     }
53234     
53235     
53236     
53237     Roo.ContentPanel.superclass.constructor.call(this);
53238     
53239     if (this.view && typeof(this.view.xtype) != 'undefined') {
53240         this.view.el = this.el.appendChild(document.createElement("div"));
53241         this.view = Roo.factory(this.view); 
53242         this.view.render  &&  this.view.render(false, '');  
53243     }
53244     
53245     
53246     this.fireEvent('render', this);
53247 };
53248
53249 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
53250     tabTip:'',
53251     setRegion : function(region){
53252         this.region = region;
53253         if(region){
53254            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
53255         }else{
53256            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
53257         } 
53258     },
53259     
53260     /**
53261      * Returns the toolbar for this Panel if one was configured. 
53262      * @return {Roo.Toolbar} 
53263      */
53264     getToolbar : function(){
53265         return this.toolbar;
53266     },
53267     
53268     setActiveState : function(active){
53269         this.active = active;
53270         if(!active){
53271             this.fireEvent("deactivate", this);
53272         }else{
53273             this.fireEvent("activate", this);
53274         }
53275     },
53276     /**
53277      * Updates this panel's element
53278      * @param {String} content The new content
53279      * @param {Boolean} loadScripts (optional) true to look for and process scripts
53280     */
53281     setContent : function(content, loadScripts){
53282         this.el.update(content, loadScripts);
53283     },
53284
53285     ignoreResize : function(w, h){
53286         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
53287             return true;
53288         }else{
53289             this.lastSize = {width: w, height: h};
53290             return false;
53291         }
53292     },
53293     /**
53294      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
53295      * @return {Roo.UpdateManager} The UpdateManager
53296      */
53297     getUpdateManager : function(){
53298         return this.el.getUpdateManager();
53299     },
53300      /**
53301      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
53302      * @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:
53303 <pre><code>
53304 panel.load({
53305     url: "your-url.php",
53306     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
53307     callback: yourFunction,
53308     scope: yourObject, //(optional scope)
53309     discardUrl: false,
53310     nocache: false,
53311     text: "Loading...",
53312     timeout: 30,
53313     scripts: false
53314 });
53315 </code></pre>
53316      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
53317      * 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.
53318      * @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}
53319      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
53320      * @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.
53321      * @return {Roo.ContentPanel} this
53322      */
53323     load : function(){
53324         var um = this.el.getUpdateManager();
53325         um.update.apply(um, arguments);
53326         return this;
53327     },
53328
53329
53330     /**
53331      * 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.
53332      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
53333      * @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)
53334      * @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)
53335      * @return {Roo.UpdateManager} The UpdateManager
53336      */
53337     setUrl : function(url, params, loadOnce){
53338         if(this.refreshDelegate){
53339             this.removeListener("activate", this.refreshDelegate);
53340         }
53341         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
53342         this.on("activate", this.refreshDelegate);
53343         return this.el.getUpdateManager();
53344     },
53345     
53346     _handleRefresh : function(url, params, loadOnce){
53347         if(!loadOnce || !this.loaded){
53348             var updater = this.el.getUpdateManager();
53349             updater.update(url, params, this._setLoaded.createDelegate(this));
53350         }
53351     },
53352     
53353     _setLoaded : function(){
53354         this.loaded = true;
53355     }, 
53356     
53357     /**
53358      * Returns this panel's id
53359      * @return {String} 
53360      */
53361     getId : function(){
53362         return this.el.id;
53363     },
53364     
53365     /** 
53366      * Returns this panel's element - used by regiosn to add.
53367      * @return {Roo.Element} 
53368      */
53369     getEl : function(){
53370         return this.wrapEl || this.el;
53371     },
53372     
53373     adjustForComponents : function(width, height)
53374     {
53375         //Roo.log('adjustForComponents ');
53376         if(this.resizeEl != this.el){
53377             width -= this.el.getFrameWidth('lr');
53378             height -= this.el.getFrameWidth('tb');
53379         }
53380         if(this.toolbar){
53381             var te = this.toolbar.getEl();
53382             height -= te.getHeight();
53383             te.setWidth(width);
53384         }
53385         if(this.footer){
53386             var te = this.footer.getEl();
53387             Roo.log("footer:" + te.getHeight());
53388             
53389             height -= te.getHeight();
53390             te.setWidth(width);
53391         }
53392         
53393         
53394         if(this.adjustments){
53395             width += this.adjustments[0];
53396             height += this.adjustments[1];
53397         }
53398         return {"width": width, "height": height};
53399     },
53400     
53401     setSize : function(width, height){
53402         if(this.fitToFrame && !this.ignoreResize(width, height)){
53403             if(this.fitContainer && this.resizeEl != this.el){
53404                 this.el.setSize(width, height);
53405             }
53406             var size = this.adjustForComponents(width, height);
53407             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
53408             this.fireEvent('resize', this, size.width, size.height);
53409         }
53410     },
53411     
53412     /**
53413      * Returns this panel's title
53414      * @return {String} 
53415      */
53416     getTitle : function(){
53417         return this.title;
53418     },
53419     
53420     /**
53421      * Set this panel's title
53422      * @param {String} title
53423      */
53424     setTitle : function(title){
53425         this.title = title;
53426         if(this.region){
53427             this.region.updatePanelTitle(this, title);
53428         }
53429     },
53430     
53431     /**
53432      * Returns true is this panel was configured to be closable
53433      * @return {Boolean} 
53434      */
53435     isClosable : function(){
53436         return this.closable;
53437     },
53438     
53439     beforeSlide : function(){
53440         this.el.clip();
53441         this.resizeEl.clip();
53442     },
53443     
53444     afterSlide : function(){
53445         this.el.unclip();
53446         this.resizeEl.unclip();
53447     },
53448     
53449     /**
53450      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
53451      *   Will fail silently if the {@link #setUrl} method has not been called.
53452      *   This does not activate the panel, just updates its content.
53453      */
53454     refresh : function(){
53455         if(this.refreshDelegate){
53456            this.loaded = false;
53457            this.refreshDelegate();
53458         }
53459     },
53460     
53461     /**
53462      * Destroys this panel
53463      */
53464     destroy : function(){
53465         this.el.removeAllListeners();
53466         var tempEl = document.createElement("span");
53467         tempEl.appendChild(this.el.dom);
53468         tempEl.innerHTML = "";
53469         this.el.remove();
53470         this.el = null;
53471     },
53472     
53473     /**
53474      * form - if the content panel contains a form - this is a reference to it.
53475      * @type {Roo.form.Form}
53476      */
53477     form : false,
53478     /**
53479      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
53480      *    This contains a reference to it.
53481      * @type {Roo.View}
53482      */
53483     view : false,
53484     
53485       /**
53486      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
53487      * <pre><code>
53488
53489 layout.addxtype({
53490        xtype : 'Form',
53491        items: [ .... ]
53492    }
53493 );
53494
53495 </code></pre>
53496      * @param {Object} cfg Xtype definition of item to add.
53497      */
53498     
53499     addxtype : function(cfg) {
53500         // add form..
53501         if (cfg.xtype.match(/^Form$/)) {
53502             
53503             var el;
53504             //if (this.footer) {
53505             //    el = this.footer.container.insertSibling(false, 'before');
53506             //} else {
53507                 el = this.el.createChild();
53508             //}
53509
53510             this.form = new  Roo.form.Form(cfg);
53511             
53512             
53513             if ( this.form.allItems.length) {
53514                 this.form.render(el.dom);
53515             }
53516             return this.form;
53517         }
53518         // should only have one of theses..
53519         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
53520             // views.. should not be just added - used named prop 'view''
53521             
53522             cfg.el = this.el.appendChild(document.createElement("div"));
53523             // factory?
53524             
53525             var ret = new Roo.factory(cfg);
53526              
53527              ret.render && ret.render(false, ''); // render blank..
53528             this.view = ret;
53529             return ret;
53530         }
53531         return false;
53532     }
53533 });
53534
53535 /**
53536  * @class Roo.GridPanel
53537  * @extends Roo.ContentPanel
53538  * @constructor
53539  * Create a new GridPanel.
53540  * @param {Roo.grid.Grid} grid The grid for this panel
53541  * @param {String/Object} config A string to set only the panel's title, or a config object
53542  */
53543 Roo.GridPanel = function(grid, config){
53544     
53545   
53546     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
53547         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
53548         
53549     this.wrapper.dom.appendChild(grid.getGridEl().dom);
53550     
53551     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
53552     
53553     if(this.toolbar){
53554         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
53555     }
53556     // xtype created footer. - not sure if will work as we normally have to render first..
53557     if (this.footer && !this.footer.el && this.footer.xtype) {
53558         
53559         this.footer.container = this.grid.getView().getFooterPanel(true);
53560         this.footer.dataSource = this.grid.dataSource;
53561         this.footer = Roo.factory(this.footer, Roo);
53562         
53563     }
53564     
53565     grid.monitorWindowResize = false; // turn off autosizing
53566     grid.autoHeight = false;
53567     grid.autoWidth = false;
53568     this.grid = grid;
53569     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
53570 };
53571
53572 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
53573     getId : function(){
53574         return this.grid.id;
53575     },
53576     
53577     /**
53578      * Returns the grid for this panel
53579      * @return {Roo.grid.Grid} 
53580      */
53581     getGrid : function(){
53582         return this.grid;    
53583     },
53584     
53585     setSize : function(width, height){
53586         if(!this.ignoreResize(width, height)){
53587             var grid = this.grid;
53588             var size = this.adjustForComponents(width, height);
53589             grid.getGridEl().setSize(size.width, size.height);
53590             grid.autoSize();
53591         }
53592     },
53593     
53594     beforeSlide : function(){
53595         this.grid.getView().scroller.clip();
53596     },
53597     
53598     afterSlide : function(){
53599         this.grid.getView().scroller.unclip();
53600     },
53601     
53602     destroy : function(){
53603         this.grid.destroy();
53604         delete this.grid;
53605         Roo.GridPanel.superclass.destroy.call(this); 
53606     }
53607 });
53608
53609
53610 /**
53611  * @class Roo.NestedLayoutPanel
53612  * @extends Roo.ContentPanel
53613  * @constructor
53614  * Create a new NestedLayoutPanel.
53615  * 
53616  * 
53617  * @param {Roo.BorderLayout} layout The layout for this panel
53618  * @param {String/Object} config A string to set only the title or a config object
53619  */
53620 Roo.NestedLayoutPanel = function(layout, config)
53621 {
53622     // construct with only one argument..
53623     /* FIXME - implement nicer consturctors
53624     if (layout.layout) {
53625         config = layout;
53626         layout = config.layout;
53627         delete config.layout;
53628     }
53629     if (layout.xtype && !layout.getEl) {
53630         // then layout needs constructing..
53631         layout = Roo.factory(layout, Roo);
53632     }
53633     */
53634     
53635     
53636     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
53637     
53638     layout.monitorWindowResize = false; // turn off autosizing
53639     this.layout = layout;
53640     this.layout.getEl().addClass("x-layout-nested-layout");
53641     
53642     
53643     
53644     
53645 };
53646
53647 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
53648
53649     setSize : function(width, height){
53650         if(!this.ignoreResize(width, height)){
53651             var size = this.adjustForComponents(width, height);
53652             var el = this.layout.getEl();
53653             el.setSize(size.width, size.height);
53654             var touch = el.dom.offsetWidth;
53655             this.layout.layout();
53656             // ie requires a double layout on the first pass
53657             if(Roo.isIE && !this.initialized){
53658                 this.initialized = true;
53659                 this.layout.layout();
53660             }
53661         }
53662     },
53663     
53664     // activate all subpanels if not currently active..
53665     
53666     setActiveState : function(active){
53667         this.active = active;
53668         if(!active){
53669             this.fireEvent("deactivate", this);
53670             return;
53671         }
53672         
53673         this.fireEvent("activate", this);
53674         // not sure if this should happen before or after..
53675         if (!this.layout) {
53676             return; // should not happen..
53677         }
53678         var reg = false;
53679         for (var r in this.layout.regions) {
53680             reg = this.layout.getRegion(r);
53681             if (reg.getActivePanel()) {
53682                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
53683                 reg.setActivePanel(reg.getActivePanel());
53684                 continue;
53685             }
53686             if (!reg.panels.length) {
53687                 continue;
53688             }
53689             reg.showPanel(reg.getPanel(0));
53690         }
53691         
53692         
53693         
53694         
53695     },
53696     
53697     /**
53698      * Returns the nested BorderLayout for this panel
53699      * @return {Roo.BorderLayout} 
53700      */
53701     getLayout : function(){
53702         return this.layout;
53703     },
53704     
53705      /**
53706      * Adds a xtype elements to the layout of the nested panel
53707      * <pre><code>
53708
53709 panel.addxtype({
53710        xtype : 'ContentPanel',
53711        region: 'west',
53712        items: [ .... ]
53713    }
53714 );
53715
53716 panel.addxtype({
53717         xtype : 'NestedLayoutPanel',
53718         region: 'west',
53719         layout: {
53720            center: { },
53721            west: { }   
53722         },
53723         items : [ ... list of content panels or nested layout panels.. ]
53724    }
53725 );
53726 </code></pre>
53727      * @param {Object} cfg Xtype definition of item to add.
53728      */
53729     addxtype : function(cfg) {
53730         return this.layout.addxtype(cfg);
53731     
53732     }
53733 });
53734
53735 Roo.ScrollPanel = function(el, config, content){
53736     config = config || {};
53737     config.fitToFrame = true;
53738     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
53739     
53740     this.el.dom.style.overflow = "hidden";
53741     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
53742     this.el.removeClass("x-layout-inactive-content");
53743     this.el.on("mousewheel", this.onWheel, this);
53744
53745     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
53746     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
53747     up.unselectable(); down.unselectable();
53748     up.on("click", this.scrollUp, this);
53749     down.on("click", this.scrollDown, this);
53750     up.addClassOnOver("x-scroller-btn-over");
53751     down.addClassOnOver("x-scroller-btn-over");
53752     up.addClassOnClick("x-scroller-btn-click");
53753     down.addClassOnClick("x-scroller-btn-click");
53754     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
53755
53756     this.resizeEl = this.el;
53757     this.el = wrap; this.up = up; this.down = down;
53758 };
53759
53760 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
53761     increment : 100,
53762     wheelIncrement : 5,
53763     scrollUp : function(){
53764         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
53765     },
53766
53767     scrollDown : function(){
53768         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
53769     },
53770
53771     afterScroll : function(){
53772         var el = this.resizeEl;
53773         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
53774         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53775         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
53776     },
53777
53778     setSize : function(){
53779         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
53780         this.afterScroll();
53781     },
53782
53783     onWheel : function(e){
53784         var d = e.getWheelDelta();
53785         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
53786         this.afterScroll();
53787         e.stopEvent();
53788     },
53789
53790     setContent : function(content, loadScripts){
53791         this.resizeEl.update(content, loadScripts);
53792     }
53793
53794 });
53795
53796
53797
53798
53799
53800
53801
53802
53803
53804 /**
53805  * @class Roo.TreePanel
53806  * @extends Roo.ContentPanel
53807  * @constructor
53808  * Create a new TreePanel. - defaults to fit/scoll contents.
53809  * @param {String/Object} config A string to set only the panel's title, or a config object
53810  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
53811  */
53812 Roo.TreePanel = function(config){
53813     var el = config.el;
53814     var tree = config.tree;
53815     delete config.tree; 
53816     delete config.el; // hopefull!
53817     
53818     // wrapper for IE7 strict & safari scroll issue
53819     
53820     var treeEl = el.createChild();
53821     config.resizeEl = treeEl;
53822     
53823     
53824     
53825     Roo.TreePanel.superclass.constructor.call(this, el, config);
53826  
53827  
53828     this.tree = new Roo.tree.TreePanel(treeEl , tree);
53829     //console.log(tree);
53830     this.on('activate', function()
53831     {
53832         if (this.tree.rendered) {
53833             return;
53834         }
53835         //console.log('render tree');
53836         this.tree.render();
53837     });
53838     // this should not be needed.. - it's actually the 'el' that resizes?
53839     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
53840     
53841     //this.on('resize',  function (cp, w, h) {
53842     //        this.tree.innerCt.setWidth(w);
53843     //        this.tree.innerCt.setHeight(h);
53844     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
53845     //});
53846
53847         
53848     
53849 };
53850
53851 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
53852     fitToFrame : true,
53853     autoScroll : true
53854 });
53855
53856
53857
53858
53859
53860
53861
53862
53863
53864
53865
53866 /*
53867  * Based on:
53868  * Ext JS Library 1.1.1
53869  * Copyright(c) 2006-2007, Ext JS, LLC.
53870  *
53871  * Originally Released Under LGPL - original licence link has changed is not relivant.
53872  *
53873  * Fork - LGPL
53874  * <script type="text/javascript">
53875  */
53876  
53877
53878 /**
53879  * @class Roo.ReaderLayout
53880  * @extends Roo.BorderLayout
53881  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
53882  * center region containing two nested regions (a top one for a list view and one for item preview below),
53883  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
53884  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
53885  * expedites the setup of the overall layout and regions for this common application style.
53886  * Example:
53887  <pre><code>
53888 var reader = new Roo.ReaderLayout();
53889 var CP = Roo.ContentPanel;  // shortcut for adding
53890
53891 reader.beginUpdate();
53892 reader.add("north", new CP("north", "North"));
53893 reader.add("west", new CP("west", {title: "West"}));
53894 reader.add("east", new CP("east", {title: "East"}));
53895
53896 reader.regions.listView.add(new CP("listView", "List"));
53897 reader.regions.preview.add(new CP("preview", "Preview"));
53898 reader.endUpdate();
53899 </code></pre>
53900 * @constructor
53901 * Create a new ReaderLayout
53902 * @param {Object} config Configuration options
53903 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
53904 * document.body if omitted)
53905 */
53906 Roo.ReaderLayout = function(config, renderTo){
53907     var c = config || {size:{}};
53908     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
53909         north: c.north !== false ? Roo.apply({
53910             split:false,
53911             initialSize: 32,
53912             titlebar: false
53913         }, c.north) : false,
53914         west: c.west !== false ? Roo.apply({
53915             split:true,
53916             initialSize: 200,
53917             minSize: 175,
53918             maxSize: 400,
53919             titlebar: true,
53920             collapsible: true,
53921             animate: true,
53922             margins:{left:5,right:0,bottom:5,top:5},
53923             cmargins:{left:5,right:5,bottom:5,top:5}
53924         }, c.west) : false,
53925         east: c.east !== 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:0,right:5,bottom:5,top:5},
53934             cmargins:{left:5,right:5,bottom:5,top:5}
53935         }, c.east) : false,
53936         center: Roo.apply({
53937             tabPosition: 'top',
53938             autoScroll:false,
53939             closeOnTab: true,
53940             titlebar:false,
53941             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
53942         }, c.center)
53943     });
53944
53945     this.el.addClass('x-reader');
53946
53947     this.beginUpdate();
53948
53949     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
53950         south: c.preview !== false ? Roo.apply({
53951             split:true,
53952             initialSize: 200,
53953             minSize: 100,
53954             autoScroll:true,
53955             collapsible:true,
53956             titlebar: true,
53957             cmargins:{top:5,left:0, right:0, bottom:0}
53958         }, c.preview) : false,
53959         center: Roo.apply({
53960             autoScroll:false,
53961             titlebar:false,
53962             minHeight:200
53963         }, c.listView)
53964     });
53965     this.add('center', new Roo.NestedLayoutPanel(inner,
53966             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
53967
53968     this.endUpdate();
53969
53970     this.regions.preview = inner.getRegion('south');
53971     this.regions.listView = inner.getRegion('center');
53972 };
53973
53974 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
53975  * Based on:
53976  * Ext JS Library 1.1.1
53977  * Copyright(c) 2006-2007, Ext JS, LLC.
53978  *
53979  * Originally Released Under LGPL - original licence link has changed is not relivant.
53980  *
53981  * Fork - LGPL
53982  * <script type="text/javascript">
53983  */
53984  
53985 /**
53986  * @class Roo.grid.Grid
53987  * @extends Roo.util.Observable
53988  * This class represents the primary interface of a component based grid control.
53989  * <br><br>Usage:<pre><code>
53990  var grid = new Roo.grid.Grid("my-container-id", {
53991      ds: myDataStore,
53992      cm: myColModel,
53993      selModel: mySelectionModel,
53994      autoSizeColumns: true,
53995      monitorWindowResize: false,
53996      trackMouseOver: true
53997  });
53998  // set any options
53999  grid.render();
54000  * </code></pre>
54001  * <b>Common Problems:</b><br/>
54002  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54003  * element will correct this<br/>
54004  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54005  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54006  * are unpredictable.<br/>
54007  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54008  * grid to calculate dimensions/offsets.<br/>
54009   * @constructor
54010  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54011  * The container MUST have some type of size defined for the grid to fill. The container will be
54012  * automatically set to position relative if it isn't already.
54013  * @param {Object} config A config object that sets properties on this grid.
54014  */
54015 Roo.grid.Grid = function(container, config){
54016         // initialize the container
54017         this.container = Roo.get(container);
54018         this.container.update("");
54019         this.container.setStyle("overflow", "hidden");
54020     this.container.addClass('x-grid-container');
54021
54022     this.id = this.container.id;
54023
54024     Roo.apply(this, config);
54025     // check and correct shorthanded configs
54026     if(this.ds){
54027         this.dataSource = this.ds;
54028         delete this.ds;
54029     }
54030     if(this.cm){
54031         this.colModel = this.cm;
54032         delete this.cm;
54033     }
54034     if(this.sm){
54035         this.selModel = this.sm;
54036         delete this.sm;
54037     }
54038
54039     if (this.selModel) {
54040         this.selModel = Roo.factory(this.selModel, Roo.grid);
54041         this.sm = this.selModel;
54042         this.sm.xmodule = this.xmodule || false;
54043     }
54044     if (typeof(this.colModel.config) == 'undefined') {
54045         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54046         this.cm = this.colModel;
54047         this.cm.xmodule = this.xmodule || false;
54048     }
54049     if (this.dataSource) {
54050         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54051         this.ds = this.dataSource;
54052         this.ds.xmodule = this.xmodule || false;
54053          
54054     }
54055     
54056     
54057     
54058     if(this.width){
54059         this.container.setWidth(this.width);
54060     }
54061
54062     if(this.height){
54063         this.container.setHeight(this.height);
54064     }
54065     /** @private */
54066         this.addEvents({
54067         // raw events
54068         /**
54069          * @event click
54070          * The raw click event for the entire grid.
54071          * @param {Roo.EventObject} e
54072          */
54073         "click" : true,
54074         /**
54075          * @event dblclick
54076          * The raw dblclick event for the entire grid.
54077          * @param {Roo.EventObject} e
54078          */
54079         "dblclick" : true,
54080         /**
54081          * @event contextmenu
54082          * The raw contextmenu event for the entire grid.
54083          * @param {Roo.EventObject} e
54084          */
54085         "contextmenu" : true,
54086         /**
54087          * @event mousedown
54088          * The raw mousedown event for the entire grid.
54089          * @param {Roo.EventObject} e
54090          */
54091         "mousedown" : true,
54092         /**
54093          * @event mouseup
54094          * The raw mouseup event for the entire grid.
54095          * @param {Roo.EventObject} e
54096          */
54097         "mouseup" : true,
54098         /**
54099          * @event mouseover
54100          * The raw mouseover event for the entire grid.
54101          * @param {Roo.EventObject} e
54102          */
54103         "mouseover" : true,
54104         /**
54105          * @event mouseout
54106          * The raw mouseout event for the entire grid.
54107          * @param {Roo.EventObject} e
54108          */
54109         "mouseout" : true,
54110         /**
54111          * @event keypress
54112          * The raw keypress event for the entire grid.
54113          * @param {Roo.EventObject} e
54114          */
54115         "keypress" : true,
54116         /**
54117          * @event keydown
54118          * The raw keydown event for the entire grid.
54119          * @param {Roo.EventObject} e
54120          */
54121         "keydown" : true,
54122
54123         // custom events
54124
54125         /**
54126          * @event cellclick
54127          * Fires when a cell is clicked
54128          * @param {Grid} this
54129          * @param {Number} rowIndex
54130          * @param {Number} columnIndex
54131          * @param {Roo.EventObject} e
54132          */
54133         "cellclick" : true,
54134         /**
54135          * @event celldblclick
54136          * Fires when a cell is double clicked
54137          * @param {Grid} this
54138          * @param {Number} rowIndex
54139          * @param {Number} columnIndex
54140          * @param {Roo.EventObject} e
54141          */
54142         "celldblclick" : true,
54143         /**
54144          * @event rowclick
54145          * Fires when a row is clicked
54146          * @param {Grid} this
54147          * @param {Number} rowIndex
54148          * @param {Roo.EventObject} e
54149          */
54150         "rowclick" : true,
54151         /**
54152          * @event rowdblclick
54153          * Fires when a row is double clicked
54154          * @param {Grid} this
54155          * @param {Number} rowIndex
54156          * @param {Roo.EventObject} e
54157          */
54158         "rowdblclick" : true,
54159         /**
54160          * @event headerclick
54161          * Fires when a header is clicked
54162          * @param {Grid} this
54163          * @param {Number} columnIndex
54164          * @param {Roo.EventObject} e
54165          */
54166         "headerclick" : true,
54167         /**
54168          * @event headerdblclick
54169          * Fires when a header cell is double clicked
54170          * @param {Grid} this
54171          * @param {Number} columnIndex
54172          * @param {Roo.EventObject} e
54173          */
54174         "headerdblclick" : true,
54175         /**
54176          * @event rowcontextmenu
54177          * Fires when a row is right clicked
54178          * @param {Grid} this
54179          * @param {Number} rowIndex
54180          * @param {Roo.EventObject} e
54181          */
54182         "rowcontextmenu" : true,
54183         /**
54184          * @event cellcontextmenu
54185          * Fires when a cell is right clicked
54186          * @param {Grid} this
54187          * @param {Number} rowIndex
54188          * @param {Number} cellIndex
54189          * @param {Roo.EventObject} e
54190          */
54191          "cellcontextmenu" : true,
54192         /**
54193          * @event headercontextmenu
54194          * Fires when a header is right clicked
54195          * @param {Grid} this
54196          * @param {Number} columnIndex
54197          * @param {Roo.EventObject} e
54198          */
54199         "headercontextmenu" : true,
54200         /**
54201          * @event bodyscroll
54202          * Fires when the body element is scrolled
54203          * @param {Number} scrollLeft
54204          * @param {Number} scrollTop
54205          */
54206         "bodyscroll" : true,
54207         /**
54208          * @event columnresize
54209          * Fires when the user resizes a column
54210          * @param {Number} columnIndex
54211          * @param {Number} newSize
54212          */
54213         "columnresize" : true,
54214         /**
54215          * @event columnmove
54216          * Fires when the user moves a column
54217          * @param {Number} oldIndex
54218          * @param {Number} newIndex
54219          */
54220         "columnmove" : true,
54221         /**
54222          * @event startdrag
54223          * Fires when row(s) start being dragged
54224          * @param {Grid} this
54225          * @param {Roo.GridDD} dd The drag drop object
54226          * @param {event} e The raw browser event
54227          */
54228         "startdrag" : true,
54229         /**
54230          * @event enddrag
54231          * Fires when a drag operation is complete
54232          * @param {Grid} this
54233          * @param {Roo.GridDD} dd The drag drop object
54234          * @param {event} e The raw browser event
54235          */
54236         "enddrag" : true,
54237         /**
54238          * @event dragdrop
54239          * Fires when dragged row(s) are dropped on a valid DD target
54240          * @param {Grid} this
54241          * @param {Roo.GridDD} dd The drag drop object
54242          * @param {String} targetId The target drag drop object
54243          * @param {event} e The raw browser event
54244          */
54245         "dragdrop" : true,
54246         /**
54247          * @event dragover
54248          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
54249          * @param {Grid} this
54250          * @param {Roo.GridDD} dd The drag drop object
54251          * @param {String} targetId The target drag drop object
54252          * @param {event} e The raw browser event
54253          */
54254         "dragover" : true,
54255         /**
54256          * @event dragenter
54257          *  Fires when the dragged row(s) first cross another DD target while being dragged
54258          * @param {Grid} this
54259          * @param {Roo.GridDD} dd The drag drop object
54260          * @param {String} targetId The target drag drop object
54261          * @param {event} e The raw browser event
54262          */
54263         "dragenter" : true,
54264         /**
54265          * @event dragout
54266          * Fires when the dragged row(s) leave another DD target while being dragged
54267          * @param {Grid} this
54268          * @param {Roo.GridDD} dd The drag drop object
54269          * @param {String} targetId The target drag drop object
54270          * @param {event} e The raw browser event
54271          */
54272         "dragout" : true,
54273         /**
54274          * @event rowclass
54275          * Fires when a row is rendered, so you can change add a style to it.
54276          * @param {GridView} gridview   The grid view
54277          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
54278          */
54279         'rowclass' : true,
54280
54281         /**
54282          * @event render
54283          * Fires when the grid is rendered
54284          * @param {Grid} grid
54285          */
54286         'render' : true
54287     });
54288
54289     Roo.grid.Grid.superclass.constructor.call(this);
54290 };
54291 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
54292     
54293     /**
54294      * @cfg {String} ddGroup - drag drop group.
54295      */
54296
54297     /**
54298      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
54299      */
54300     minColumnWidth : 25,
54301
54302     /**
54303      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
54304      * <b>on initial render.</b> It is more efficient to explicitly size the columns
54305      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
54306      */
54307     autoSizeColumns : false,
54308
54309     /**
54310      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
54311      */
54312     autoSizeHeaders : true,
54313
54314     /**
54315      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
54316      */
54317     monitorWindowResize : true,
54318
54319     /**
54320      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
54321      * rows measured to get a columns size. Default is 0 (all rows).
54322      */
54323     maxRowsToMeasure : 0,
54324
54325     /**
54326      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
54327      */
54328     trackMouseOver : true,
54329
54330     /**
54331     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
54332     */
54333     
54334     /**
54335     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
54336     */
54337     enableDragDrop : false,
54338     
54339     /**
54340     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
54341     */
54342     enableColumnMove : true,
54343     
54344     /**
54345     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
54346     */
54347     enableColumnHide : true,
54348     
54349     /**
54350     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
54351     */
54352     enableRowHeightSync : false,
54353     
54354     /**
54355     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
54356     */
54357     stripeRows : true,
54358     
54359     /**
54360     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
54361     */
54362     autoHeight : false,
54363
54364     /**
54365      * @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.
54366      */
54367     autoExpandColumn : false,
54368
54369     /**
54370     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
54371     * Default is 50.
54372     */
54373     autoExpandMin : 50,
54374
54375     /**
54376     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
54377     */
54378     autoExpandMax : 1000,
54379
54380     /**
54381     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
54382     */
54383     view : null,
54384
54385     /**
54386     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
54387     */
54388     loadMask : false,
54389     /**
54390     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
54391     */
54392     dropTarget: false,
54393     
54394    
54395     
54396     // private
54397     rendered : false,
54398
54399     /**
54400     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
54401     * of a fixed width. Default is false.
54402     */
54403     /**
54404     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
54405     */
54406     /**
54407      * Called once after all setup has been completed and the grid is ready to be rendered.
54408      * @return {Roo.grid.Grid} this
54409      */
54410     render : function()
54411     {
54412         var c = this.container;
54413         // try to detect autoHeight/width mode
54414         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
54415             this.autoHeight = true;
54416         }
54417         var view = this.getView();
54418         view.init(this);
54419
54420         c.on("click", this.onClick, this);
54421         c.on("dblclick", this.onDblClick, this);
54422         c.on("contextmenu", this.onContextMenu, this);
54423         c.on("keydown", this.onKeyDown, this);
54424         if (Roo.isTouch) {
54425             c.on("touchstart", this.onTouchStart, this);
54426         }
54427
54428         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
54429
54430         this.getSelectionModel().init(this);
54431
54432         view.render();
54433
54434         if(this.loadMask){
54435             this.loadMask = new Roo.LoadMask(this.container,
54436                     Roo.apply({store:this.dataSource}, this.loadMask));
54437         }
54438         
54439         
54440         if (this.toolbar && this.toolbar.xtype) {
54441             this.toolbar.container = this.getView().getHeaderPanel(true);
54442             this.toolbar = new Roo.Toolbar(this.toolbar);
54443         }
54444         if (this.footer && this.footer.xtype) {
54445             this.footer.dataSource = this.getDataSource();
54446             this.footer.container = this.getView().getFooterPanel(true);
54447             this.footer = Roo.factory(this.footer, Roo);
54448         }
54449         if (this.dropTarget && this.dropTarget.xtype) {
54450             delete this.dropTarget.xtype;
54451             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
54452         }
54453         
54454         
54455         this.rendered = true;
54456         this.fireEvent('render', this);
54457         return this;
54458     },
54459
54460         /**
54461          * Reconfigures the grid to use a different Store and Column Model.
54462          * The View will be bound to the new objects and refreshed.
54463          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
54464          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
54465          */
54466     reconfigure : function(dataSource, colModel){
54467         if(this.loadMask){
54468             this.loadMask.destroy();
54469             this.loadMask = new Roo.LoadMask(this.container,
54470                     Roo.apply({store:dataSource}, this.loadMask));
54471         }
54472         this.view.bind(dataSource, colModel);
54473         this.dataSource = dataSource;
54474         this.colModel = colModel;
54475         this.view.refresh(true);
54476     },
54477
54478     // private
54479     onKeyDown : function(e){
54480         this.fireEvent("keydown", e);
54481     },
54482
54483     /**
54484      * Destroy this grid.
54485      * @param {Boolean} removeEl True to remove the element
54486      */
54487     destroy : function(removeEl, keepListeners){
54488         if(this.loadMask){
54489             this.loadMask.destroy();
54490         }
54491         var c = this.container;
54492         c.removeAllListeners();
54493         this.view.destroy();
54494         this.colModel.purgeListeners();
54495         if(!keepListeners){
54496             this.purgeListeners();
54497         }
54498         c.update("");
54499         if(removeEl === true){
54500             c.remove();
54501         }
54502     },
54503
54504     // private
54505     processEvent : function(name, e){
54506         // does this fire select???
54507         //Roo.log('grid:processEvent '  + name);
54508         
54509         if (name != 'touchstart' ) {
54510             this.fireEvent(name, e);    
54511         }
54512         
54513         var t = e.getTarget();
54514         var v = this.view;
54515         var header = v.findHeaderIndex(t);
54516         if(header !== false){
54517             var ename = name == 'touchstart' ? 'click' : name;
54518              
54519             this.fireEvent("header" + ename, this, header, e);
54520         }else{
54521             var row = v.findRowIndex(t);
54522             var cell = v.findCellIndex(t);
54523             if (name == 'touchstart') {
54524                 // first touch is always a click.
54525                 // hopefull this happens after selection is updated.?
54526                 name = false;
54527                 
54528                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
54529                     var cs = this.selModel.getSelectedCell();
54530                     if (row == cs[0] && cell == cs[1]){
54531                         name = 'dblclick';
54532                     }
54533                 }
54534                 if (typeof(this.selModel.getSelections) != 'undefined') {
54535                     var cs = this.selModel.getSelections();
54536                     var ds = this.dataSource;
54537                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
54538                         name = 'dblclick';
54539                     }
54540                 }
54541                 if (!name) {
54542                     return;
54543                 }
54544             }
54545             
54546             
54547             if(row !== false){
54548                 this.fireEvent("row" + name, this, row, e);
54549                 if(cell !== false){
54550                     this.fireEvent("cell" + name, this, row, cell, e);
54551                 }
54552             }
54553         }
54554     },
54555
54556     // private
54557     onClick : function(e){
54558         this.processEvent("click", e);
54559     },
54560    // private
54561     onTouchStart : function(e){
54562         this.processEvent("touchstart", e);
54563     },
54564
54565     // private
54566     onContextMenu : function(e, t){
54567         this.processEvent("contextmenu", e);
54568     },
54569
54570     // private
54571     onDblClick : function(e){
54572         this.processEvent("dblclick", e);
54573     },
54574
54575     // private
54576     walkCells : function(row, col, step, fn, scope){
54577         var cm = this.colModel, clen = cm.getColumnCount();
54578         var ds = this.dataSource, rlen = ds.getCount(), first = true;
54579         if(step < 0){
54580             if(col < 0){
54581                 row--;
54582                 first = false;
54583             }
54584             while(row >= 0){
54585                 if(!first){
54586                     col = clen-1;
54587                 }
54588                 first = false;
54589                 while(col >= 0){
54590                     if(fn.call(scope || this, row, col, cm) === true){
54591                         return [row, col];
54592                     }
54593                     col--;
54594                 }
54595                 row--;
54596             }
54597         } else {
54598             if(col >= clen){
54599                 row++;
54600                 first = false;
54601             }
54602             while(row < rlen){
54603                 if(!first){
54604                     col = 0;
54605                 }
54606                 first = false;
54607                 while(col < clen){
54608                     if(fn.call(scope || this, row, col, cm) === true){
54609                         return [row, col];
54610                     }
54611                     col++;
54612                 }
54613                 row++;
54614             }
54615         }
54616         return null;
54617     },
54618
54619     // private
54620     getSelections : function(){
54621         return this.selModel.getSelections();
54622     },
54623
54624     /**
54625      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
54626      * but if manual update is required this method will initiate it.
54627      */
54628     autoSize : function(){
54629         if(this.rendered){
54630             this.view.layout();
54631             if(this.view.adjustForScroll){
54632                 this.view.adjustForScroll();
54633             }
54634         }
54635     },
54636
54637     /**
54638      * Returns the grid's underlying element.
54639      * @return {Element} The element
54640      */
54641     getGridEl : function(){
54642         return this.container;
54643     },
54644
54645     // private for compatibility, overridden by editor grid
54646     stopEditing : function(){},
54647
54648     /**
54649      * Returns the grid's SelectionModel.
54650      * @return {SelectionModel}
54651      */
54652     getSelectionModel : function(){
54653         if(!this.selModel){
54654             this.selModel = new Roo.grid.RowSelectionModel();
54655         }
54656         return this.selModel;
54657     },
54658
54659     /**
54660      * Returns the grid's DataSource.
54661      * @return {DataSource}
54662      */
54663     getDataSource : function(){
54664         return this.dataSource;
54665     },
54666
54667     /**
54668      * Returns the grid's ColumnModel.
54669      * @return {ColumnModel}
54670      */
54671     getColumnModel : function(){
54672         return this.colModel;
54673     },
54674
54675     /**
54676      * Returns the grid's GridView object.
54677      * @return {GridView}
54678      */
54679     getView : function(){
54680         if(!this.view){
54681             this.view = new Roo.grid.GridView(this.viewConfig);
54682         }
54683         return this.view;
54684     },
54685     /**
54686      * Called to get grid's drag proxy text, by default returns this.ddText.
54687      * @return {String}
54688      */
54689     getDragDropText : function(){
54690         var count = this.selModel.getCount();
54691         return String.format(this.ddText, count, count == 1 ? '' : 's');
54692     }
54693 });
54694 /**
54695  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
54696  * %0 is replaced with the number of selected rows.
54697  * @type String
54698  */
54699 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
54700  * Based on:
54701  * Ext JS Library 1.1.1
54702  * Copyright(c) 2006-2007, Ext JS, LLC.
54703  *
54704  * Originally Released Under LGPL - original licence link has changed is not relivant.
54705  *
54706  * Fork - LGPL
54707  * <script type="text/javascript">
54708  */
54709  
54710 Roo.grid.AbstractGridView = function(){
54711         this.grid = null;
54712         
54713         this.events = {
54714             "beforerowremoved" : true,
54715             "beforerowsinserted" : true,
54716             "beforerefresh" : true,
54717             "rowremoved" : true,
54718             "rowsinserted" : true,
54719             "rowupdated" : true,
54720             "refresh" : true
54721         };
54722     Roo.grid.AbstractGridView.superclass.constructor.call(this);
54723 };
54724
54725 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
54726     rowClass : "x-grid-row",
54727     cellClass : "x-grid-cell",
54728     tdClass : "x-grid-td",
54729     hdClass : "x-grid-hd",
54730     splitClass : "x-grid-hd-split",
54731     
54732     init: function(grid){
54733         this.grid = grid;
54734                 var cid = this.grid.getGridEl().id;
54735         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
54736         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
54737         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
54738         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
54739         },
54740         
54741     getColumnRenderers : function(){
54742         var renderers = [];
54743         var cm = this.grid.colModel;
54744         var colCount = cm.getColumnCount();
54745         for(var i = 0; i < colCount; i++){
54746             renderers[i] = cm.getRenderer(i);
54747         }
54748         return renderers;
54749     },
54750     
54751     getColumnIds : function(){
54752         var ids = [];
54753         var cm = this.grid.colModel;
54754         var colCount = cm.getColumnCount();
54755         for(var i = 0; i < colCount; i++){
54756             ids[i] = cm.getColumnId(i);
54757         }
54758         return ids;
54759     },
54760     
54761     getDataIndexes : function(){
54762         if(!this.indexMap){
54763             this.indexMap = this.buildIndexMap();
54764         }
54765         return this.indexMap.colToData;
54766     },
54767     
54768     getColumnIndexByDataIndex : function(dataIndex){
54769         if(!this.indexMap){
54770             this.indexMap = this.buildIndexMap();
54771         }
54772         return this.indexMap.dataToCol[dataIndex];
54773     },
54774     
54775     /**
54776      * Set a css style for a column dynamically. 
54777      * @param {Number} colIndex The index of the column
54778      * @param {String} name The css property name
54779      * @param {String} value The css value
54780      */
54781     setCSSStyle : function(colIndex, name, value){
54782         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
54783         Roo.util.CSS.updateRule(selector, name, value);
54784     },
54785     
54786     generateRules : function(cm){
54787         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
54788         Roo.util.CSS.removeStyleSheet(rulesId);
54789         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54790             var cid = cm.getColumnId(i);
54791             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
54792                          this.tdSelector, cid, " {\n}\n",
54793                          this.hdSelector, cid, " {\n}\n",
54794                          this.splitSelector, cid, " {\n}\n");
54795         }
54796         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54797     }
54798 });/*
54799  * Based on:
54800  * Ext JS Library 1.1.1
54801  * Copyright(c) 2006-2007, Ext JS, LLC.
54802  *
54803  * Originally Released Under LGPL - original licence link has changed is not relivant.
54804  *
54805  * Fork - LGPL
54806  * <script type="text/javascript">
54807  */
54808
54809 // private
54810 // This is a support class used internally by the Grid components
54811 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
54812     this.grid = grid;
54813     this.view = grid.getView();
54814     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54815     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
54816     if(hd2){
54817         this.setHandleElId(Roo.id(hd));
54818         this.setOuterHandleElId(Roo.id(hd2));
54819     }
54820     this.scroll = false;
54821 };
54822 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
54823     maxDragWidth: 120,
54824     getDragData : function(e){
54825         var t = Roo.lib.Event.getTarget(e);
54826         var h = this.view.findHeaderCell(t);
54827         if(h){
54828             return {ddel: h.firstChild, header:h};
54829         }
54830         return false;
54831     },
54832
54833     onInitDrag : function(e){
54834         this.view.headersDisabled = true;
54835         var clone = this.dragData.ddel.cloneNode(true);
54836         clone.id = Roo.id();
54837         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
54838         this.proxy.update(clone);
54839         return true;
54840     },
54841
54842     afterValidDrop : function(){
54843         var v = this.view;
54844         setTimeout(function(){
54845             v.headersDisabled = false;
54846         }, 50);
54847     },
54848
54849     afterInvalidDrop : function(){
54850         var v = this.view;
54851         setTimeout(function(){
54852             v.headersDisabled = false;
54853         }, 50);
54854     }
54855 });
54856 /*
54857  * Based on:
54858  * Ext JS Library 1.1.1
54859  * Copyright(c) 2006-2007, Ext JS, LLC.
54860  *
54861  * Originally Released Under LGPL - original licence link has changed is not relivant.
54862  *
54863  * Fork - LGPL
54864  * <script type="text/javascript">
54865  */
54866 // private
54867 // This is a support class used internally by the Grid components
54868 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
54869     this.grid = grid;
54870     this.view = grid.getView();
54871     // split the proxies so they don't interfere with mouse events
54872     this.proxyTop = Roo.DomHelper.append(document.body, {
54873         cls:"col-move-top", html:"&#160;"
54874     }, true);
54875     this.proxyBottom = Roo.DomHelper.append(document.body, {
54876         cls:"col-move-bottom", html:"&#160;"
54877     }, true);
54878     this.proxyTop.hide = this.proxyBottom.hide = function(){
54879         this.setLeftTop(-100,-100);
54880         this.setStyle("visibility", "hidden");
54881     };
54882     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
54883     // temporarily disabled
54884     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
54885     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
54886 };
54887 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
54888     proxyOffsets : [-4, -9],
54889     fly: Roo.Element.fly,
54890
54891     getTargetFromEvent : function(e){
54892         var t = Roo.lib.Event.getTarget(e);
54893         var cindex = this.view.findCellIndex(t);
54894         if(cindex !== false){
54895             return this.view.getHeaderCell(cindex);
54896         }
54897         return null;
54898     },
54899
54900     nextVisible : function(h){
54901         var v = this.view, cm = this.grid.colModel;
54902         h = h.nextSibling;
54903         while(h){
54904             if(!cm.isHidden(v.getCellIndex(h))){
54905                 return h;
54906             }
54907             h = h.nextSibling;
54908         }
54909         return null;
54910     },
54911
54912     prevVisible : function(h){
54913         var v = this.view, cm = this.grid.colModel;
54914         h = h.prevSibling;
54915         while(h){
54916             if(!cm.isHidden(v.getCellIndex(h))){
54917                 return h;
54918             }
54919             h = h.prevSibling;
54920         }
54921         return null;
54922     },
54923
54924     positionIndicator : function(h, n, e){
54925         var x = Roo.lib.Event.getPageX(e);
54926         var r = Roo.lib.Dom.getRegion(n.firstChild);
54927         var px, pt, py = r.top + this.proxyOffsets[1];
54928         if((r.right - x) <= (r.right-r.left)/2){
54929             px = r.right+this.view.borderWidth;
54930             pt = "after";
54931         }else{
54932             px = r.left;
54933             pt = "before";
54934         }
54935         var oldIndex = this.view.getCellIndex(h);
54936         var newIndex = this.view.getCellIndex(n);
54937
54938         if(this.grid.colModel.isFixed(newIndex)){
54939             return false;
54940         }
54941
54942         var locked = this.grid.colModel.isLocked(newIndex);
54943
54944         if(pt == "after"){
54945             newIndex++;
54946         }
54947         if(oldIndex < newIndex){
54948             newIndex--;
54949         }
54950         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
54951             return false;
54952         }
54953         px +=  this.proxyOffsets[0];
54954         this.proxyTop.setLeftTop(px, py);
54955         this.proxyTop.show();
54956         if(!this.bottomOffset){
54957             this.bottomOffset = this.view.mainHd.getHeight();
54958         }
54959         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
54960         this.proxyBottom.show();
54961         return pt;
54962     },
54963
54964     onNodeEnter : function(n, dd, e, data){
54965         if(data.header != n){
54966             this.positionIndicator(data.header, n, e);
54967         }
54968     },
54969
54970     onNodeOver : function(n, dd, e, data){
54971         var result = false;
54972         if(data.header != n){
54973             result = this.positionIndicator(data.header, n, e);
54974         }
54975         if(!result){
54976             this.proxyTop.hide();
54977             this.proxyBottom.hide();
54978         }
54979         return result ? this.dropAllowed : this.dropNotAllowed;
54980     },
54981
54982     onNodeOut : function(n, dd, e, data){
54983         this.proxyTop.hide();
54984         this.proxyBottom.hide();
54985     },
54986
54987     onNodeDrop : function(n, dd, e, data){
54988         var h = data.header;
54989         if(h != n){
54990             var cm = this.grid.colModel;
54991             var x = Roo.lib.Event.getPageX(e);
54992             var r = Roo.lib.Dom.getRegion(n.firstChild);
54993             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
54994             var oldIndex = this.view.getCellIndex(h);
54995             var newIndex = this.view.getCellIndex(n);
54996             var locked = cm.isLocked(newIndex);
54997             if(pt == "after"){
54998                 newIndex++;
54999             }
55000             if(oldIndex < newIndex){
55001                 newIndex--;
55002             }
55003             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55004                 return false;
55005             }
55006             cm.setLocked(oldIndex, locked, true);
55007             cm.moveColumn(oldIndex, newIndex);
55008             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55009             return true;
55010         }
55011         return false;
55012     }
55013 });
55014 /*
55015  * Based on:
55016  * Ext JS Library 1.1.1
55017  * Copyright(c) 2006-2007, Ext JS, LLC.
55018  *
55019  * Originally Released Under LGPL - original licence link has changed is not relivant.
55020  *
55021  * Fork - LGPL
55022  * <script type="text/javascript">
55023  */
55024   
55025 /**
55026  * @class Roo.grid.GridView
55027  * @extends Roo.util.Observable
55028  *
55029  * @constructor
55030  * @param {Object} config
55031  */
55032 Roo.grid.GridView = function(config){
55033     Roo.grid.GridView.superclass.constructor.call(this);
55034     this.el = null;
55035
55036     Roo.apply(this, config);
55037 };
55038
55039 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55040
55041     unselectable :  'unselectable="on"',
55042     unselectableCls :  'x-unselectable',
55043     
55044     
55045     rowClass : "x-grid-row",
55046
55047     cellClass : "x-grid-col",
55048
55049     tdClass : "x-grid-td",
55050
55051     hdClass : "x-grid-hd",
55052
55053     splitClass : "x-grid-split",
55054
55055     sortClasses : ["sort-asc", "sort-desc"],
55056
55057     enableMoveAnim : false,
55058
55059     hlColor: "C3DAF9",
55060
55061     dh : Roo.DomHelper,
55062
55063     fly : Roo.Element.fly,
55064
55065     css : Roo.util.CSS,
55066
55067     borderWidth: 1,
55068
55069     splitOffset: 3,
55070
55071     scrollIncrement : 22,
55072
55073     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55074
55075     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55076
55077     bind : function(ds, cm){
55078         if(this.ds){
55079             this.ds.un("load", this.onLoad, this);
55080             this.ds.un("datachanged", this.onDataChange, this);
55081             this.ds.un("add", this.onAdd, this);
55082             this.ds.un("remove", this.onRemove, this);
55083             this.ds.un("update", this.onUpdate, this);
55084             this.ds.un("clear", this.onClear, this);
55085         }
55086         if(ds){
55087             ds.on("load", this.onLoad, this);
55088             ds.on("datachanged", this.onDataChange, this);
55089             ds.on("add", this.onAdd, this);
55090             ds.on("remove", this.onRemove, this);
55091             ds.on("update", this.onUpdate, this);
55092             ds.on("clear", this.onClear, this);
55093         }
55094         this.ds = ds;
55095
55096         if(this.cm){
55097             this.cm.un("widthchange", this.onColWidthChange, this);
55098             this.cm.un("headerchange", this.onHeaderChange, this);
55099             this.cm.un("hiddenchange", this.onHiddenChange, this);
55100             this.cm.un("columnmoved", this.onColumnMove, this);
55101             this.cm.un("columnlockchange", this.onColumnLock, this);
55102         }
55103         if(cm){
55104             this.generateRules(cm);
55105             cm.on("widthchange", this.onColWidthChange, this);
55106             cm.on("headerchange", this.onHeaderChange, this);
55107             cm.on("hiddenchange", this.onHiddenChange, this);
55108             cm.on("columnmoved", this.onColumnMove, this);
55109             cm.on("columnlockchange", this.onColumnLock, this);
55110         }
55111         this.cm = cm;
55112     },
55113
55114     init: function(grid){
55115         Roo.grid.GridView.superclass.init.call(this, grid);
55116
55117         this.bind(grid.dataSource, grid.colModel);
55118
55119         grid.on("headerclick", this.handleHeaderClick, this);
55120
55121         if(grid.trackMouseOver){
55122             grid.on("mouseover", this.onRowOver, this);
55123             grid.on("mouseout", this.onRowOut, this);
55124         }
55125         grid.cancelTextSelection = function(){};
55126         this.gridId = grid.id;
55127
55128         var tpls = this.templates || {};
55129
55130         if(!tpls.master){
55131             tpls.master = new Roo.Template(
55132                '<div class="x-grid" hidefocus="true">',
55133                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55134                   '<div class="x-grid-topbar"></div>',
55135                   '<div class="x-grid-scroller"><div></div></div>',
55136                   '<div class="x-grid-locked">',
55137                       '<div class="x-grid-header">{lockedHeader}</div>',
55138                       '<div class="x-grid-body">{lockedBody}</div>',
55139                   "</div>",
55140                   '<div class="x-grid-viewport">',
55141                       '<div class="x-grid-header">{header}</div>',
55142                       '<div class="x-grid-body">{body}</div>',
55143                   "</div>",
55144                   '<div class="x-grid-bottombar"></div>',
55145                  
55146                   '<div class="x-grid-resize-proxy">&#160;</div>',
55147                "</div>"
55148             );
55149             tpls.master.disableformats = true;
55150         }
55151
55152         if(!tpls.header){
55153             tpls.header = new Roo.Template(
55154                '<table border="0" cellspacing="0" cellpadding="0">',
55155                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
55156                "</table>{splits}"
55157             );
55158             tpls.header.disableformats = true;
55159         }
55160         tpls.header.compile();
55161
55162         if(!tpls.hcell){
55163             tpls.hcell = new Roo.Template(
55164                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
55165                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
55166                 "</div></td>"
55167              );
55168              tpls.hcell.disableFormats = true;
55169         }
55170         tpls.hcell.compile();
55171
55172         if(!tpls.hsplit){
55173             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
55174                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
55175             tpls.hsplit.disableFormats = true;
55176         }
55177         tpls.hsplit.compile();
55178
55179         if(!tpls.body){
55180             tpls.body = new Roo.Template(
55181                '<table border="0" cellspacing="0" cellpadding="0">',
55182                "<tbody>{rows}</tbody>",
55183                "</table>"
55184             );
55185             tpls.body.disableFormats = true;
55186         }
55187         tpls.body.compile();
55188
55189         if(!tpls.row){
55190             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
55191             tpls.row.disableFormats = true;
55192         }
55193         tpls.row.compile();
55194
55195         if(!tpls.cell){
55196             tpls.cell = new Roo.Template(
55197                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
55198                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
55199                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
55200                 "</td>"
55201             );
55202             tpls.cell.disableFormats = true;
55203         }
55204         tpls.cell.compile();
55205
55206         this.templates = tpls;
55207     },
55208
55209     // remap these for backwards compat
55210     onColWidthChange : function(){
55211         this.updateColumns.apply(this, arguments);
55212     },
55213     onHeaderChange : function(){
55214         this.updateHeaders.apply(this, arguments);
55215     }, 
55216     onHiddenChange : function(){
55217         this.handleHiddenChange.apply(this, arguments);
55218     },
55219     onColumnMove : function(){
55220         this.handleColumnMove.apply(this, arguments);
55221     },
55222     onColumnLock : function(){
55223         this.handleLockChange.apply(this, arguments);
55224     },
55225
55226     onDataChange : function(){
55227         this.refresh();
55228         this.updateHeaderSortState();
55229     },
55230
55231     onClear : function(){
55232         this.refresh();
55233     },
55234
55235     onUpdate : function(ds, record){
55236         this.refreshRow(record);
55237     },
55238
55239     refreshRow : function(record){
55240         var ds = this.ds, index;
55241         if(typeof record == 'number'){
55242             index = record;
55243             record = ds.getAt(index);
55244         }else{
55245             index = ds.indexOf(record);
55246         }
55247         this.insertRows(ds, index, index, true);
55248         this.onRemove(ds, record, index+1, true);
55249         this.syncRowHeights(index, index);
55250         this.layout();
55251         this.fireEvent("rowupdated", this, index, record);
55252     },
55253
55254     onAdd : function(ds, records, index){
55255         this.insertRows(ds, index, index + (records.length-1));
55256     },
55257
55258     onRemove : function(ds, record, index, isUpdate){
55259         if(isUpdate !== true){
55260             this.fireEvent("beforerowremoved", this, index, record);
55261         }
55262         var bt = this.getBodyTable(), lt = this.getLockedTable();
55263         if(bt.rows[index]){
55264             bt.firstChild.removeChild(bt.rows[index]);
55265         }
55266         if(lt.rows[index]){
55267             lt.firstChild.removeChild(lt.rows[index]);
55268         }
55269         if(isUpdate !== true){
55270             this.stripeRows(index);
55271             this.syncRowHeights(index, index);
55272             this.layout();
55273             this.fireEvent("rowremoved", this, index, record);
55274         }
55275     },
55276
55277     onLoad : function(){
55278         this.scrollToTop();
55279     },
55280
55281     /**
55282      * Scrolls the grid to the top
55283      */
55284     scrollToTop : function(){
55285         if(this.scroller){
55286             this.scroller.dom.scrollTop = 0;
55287             this.syncScroll();
55288         }
55289     },
55290
55291     /**
55292      * Gets a panel in the header of the grid that can be used for toolbars etc.
55293      * After modifying the contents of this panel a call to grid.autoSize() may be
55294      * required to register any changes in size.
55295      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
55296      * @return Roo.Element
55297      */
55298     getHeaderPanel : function(doShow){
55299         if(doShow){
55300             this.headerPanel.show();
55301         }
55302         return this.headerPanel;
55303     },
55304
55305     /**
55306      * Gets a panel in the footer of the grid that can be used for toolbars etc.
55307      * After modifying the contents of this panel a call to grid.autoSize() may be
55308      * required to register any changes in size.
55309      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
55310      * @return Roo.Element
55311      */
55312     getFooterPanel : function(doShow){
55313         if(doShow){
55314             this.footerPanel.show();
55315         }
55316         return this.footerPanel;
55317     },
55318
55319     initElements : function(){
55320         var E = Roo.Element;
55321         var el = this.grid.getGridEl().dom.firstChild;
55322         var cs = el.childNodes;
55323
55324         this.el = new E(el);
55325         
55326          this.focusEl = new E(el.firstChild);
55327         this.focusEl.swallowEvent("click", true);
55328         
55329         this.headerPanel = new E(cs[1]);
55330         this.headerPanel.enableDisplayMode("block");
55331
55332         this.scroller = new E(cs[2]);
55333         this.scrollSizer = new E(this.scroller.dom.firstChild);
55334
55335         this.lockedWrap = new E(cs[3]);
55336         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
55337         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
55338
55339         this.mainWrap = new E(cs[4]);
55340         this.mainHd = new E(this.mainWrap.dom.firstChild);
55341         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
55342
55343         this.footerPanel = new E(cs[5]);
55344         this.footerPanel.enableDisplayMode("block");
55345
55346         this.resizeProxy = new E(cs[6]);
55347
55348         this.headerSelector = String.format(
55349            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
55350            this.lockedHd.id, this.mainHd.id
55351         );
55352
55353         this.splitterSelector = String.format(
55354            '#{0} div.x-grid-split, #{1} div.x-grid-split',
55355            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
55356         );
55357     },
55358     idToCssName : function(s)
55359     {
55360         return s.replace(/[^a-z0-9]+/ig, '-');
55361     },
55362
55363     getHeaderCell : function(index){
55364         return Roo.DomQuery.select(this.headerSelector)[index];
55365     },
55366
55367     getHeaderCellMeasure : function(index){
55368         return this.getHeaderCell(index).firstChild;
55369     },
55370
55371     getHeaderCellText : function(index){
55372         return this.getHeaderCell(index).firstChild.firstChild;
55373     },
55374
55375     getLockedTable : function(){
55376         return this.lockedBody.dom.firstChild;
55377     },
55378
55379     getBodyTable : function(){
55380         return this.mainBody.dom.firstChild;
55381     },
55382
55383     getLockedRow : function(index){
55384         return this.getLockedTable().rows[index];
55385     },
55386
55387     getRow : function(index){
55388         return this.getBodyTable().rows[index];
55389     },
55390
55391     getRowComposite : function(index){
55392         if(!this.rowEl){
55393             this.rowEl = new Roo.CompositeElementLite();
55394         }
55395         var els = [], lrow, mrow;
55396         if(lrow = this.getLockedRow(index)){
55397             els.push(lrow);
55398         }
55399         if(mrow = this.getRow(index)){
55400             els.push(mrow);
55401         }
55402         this.rowEl.elements = els;
55403         return this.rowEl;
55404     },
55405     /**
55406      * Gets the 'td' of the cell
55407      * 
55408      * @param {Integer} rowIndex row to select
55409      * @param {Integer} colIndex column to select
55410      * 
55411      * @return {Object} 
55412      */
55413     getCell : function(rowIndex, colIndex){
55414         var locked = this.cm.getLockedCount();
55415         var source;
55416         if(colIndex < locked){
55417             source = this.lockedBody.dom.firstChild;
55418         }else{
55419             source = this.mainBody.dom.firstChild;
55420             colIndex -= locked;
55421         }
55422         return source.rows[rowIndex].childNodes[colIndex];
55423     },
55424
55425     getCellText : function(rowIndex, colIndex){
55426         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
55427     },
55428
55429     getCellBox : function(cell){
55430         var b = this.fly(cell).getBox();
55431         if(Roo.isOpera){ // opera fails to report the Y
55432             b.y = cell.offsetTop + this.mainBody.getY();
55433         }
55434         return b;
55435     },
55436
55437     getCellIndex : function(cell){
55438         var id = String(cell.className).match(this.cellRE);
55439         if(id){
55440             return parseInt(id[1], 10);
55441         }
55442         return 0;
55443     },
55444
55445     findHeaderIndex : function(n){
55446         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55447         return r ? this.getCellIndex(r) : false;
55448     },
55449
55450     findHeaderCell : function(n){
55451         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
55452         return r ? r : false;
55453     },
55454
55455     findRowIndex : function(n){
55456         if(!n){
55457             return false;
55458         }
55459         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
55460         return r ? r.rowIndex : false;
55461     },
55462
55463     findCellIndex : function(node){
55464         var stop = this.el.dom;
55465         while(node && node != stop){
55466             if(this.findRE.test(node.className)){
55467                 return this.getCellIndex(node);
55468             }
55469             node = node.parentNode;
55470         }
55471         return false;
55472     },
55473
55474     getColumnId : function(index){
55475         return this.cm.getColumnId(index);
55476     },
55477
55478     getSplitters : function()
55479     {
55480         if(this.splitterSelector){
55481            return Roo.DomQuery.select(this.splitterSelector);
55482         }else{
55483             return null;
55484       }
55485     },
55486
55487     getSplitter : function(index){
55488         return this.getSplitters()[index];
55489     },
55490
55491     onRowOver : function(e, t){
55492         var row;
55493         if((row = this.findRowIndex(t)) !== false){
55494             this.getRowComposite(row).addClass("x-grid-row-over");
55495         }
55496     },
55497
55498     onRowOut : function(e, t){
55499         var row;
55500         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
55501             this.getRowComposite(row).removeClass("x-grid-row-over");
55502         }
55503     },
55504
55505     renderHeaders : function(){
55506         var cm = this.cm;
55507         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
55508         var cb = [], lb = [], sb = [], lsb = [], p = {};
55509         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55510             p.cellId = "x-grid-hd-0-" + i;
55511             p.splitId = "x-grid-csplit-0-" + i;
55512             p.id = cm.getColumnId(i);
55513             p.value = cm.getColumnHeader(i) || "";
55514             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
55515             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
55516             if(!cm.isLocked(i)){
55517                 cb[cb.length] = ct.apply(p);
55518                 sb[sb.length] = st.apply(p);
55519             }else{
55520                 lb[lb.length] = ct.apply(p);
55521                 lsb[lsb.length] = st.apply(p);
55522             }
55523         }
55524         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
55525                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
55526     },
55527
55528     updateHeaders : function(){
55529         var html = this.renderHeaders();
55530         this.lockedHd.update(html[0]);
55531         this.mainHd.update(html[1]);
55532     },
55533
55534     /**
55535      * Focuses the specified row.
55536      * @param {Number} row The row index
55537      */
55538     focusRow : function(row)
55539     {
55540         //Roo.log('GridView.focusRow');
55541         var x = this.scroller.dom.scrollLeft;
55542         this.focusCell(row, 0, false);
55543         this.scroller.dom.scrollLeft = x;
55544     },
55545
55546     /**
55547      * Focuses the specified cell.
55548      * @param {Number} row The row index
55549      * @param {Number} col The column index
55550      * @param {Boolean} hscroll false to disable horizontal scrolling
55551      */
55552     focusCell : function(row, col, hscroll)
55553     {
55554         //Roo.log('GridView.focusCell');
55555         var el = this.ensureVisible(row, col, hscroll);
55556         this.focusEl.alignTo(el, "tl-tl");
55557         if(Roo.isGecko){
55558             this.focusEl.focus();
55559         }else{
55560             this.focusEl.focus.defer(1, this.focusEl);
55561         }
55562     },
55563
55564     /**
55565      * Scrolls the specified cell into view
55566      * @param {Number} row The row index
55567      * @param {Number} col The column index
55568      * @param {Boolean} hscroll false to disable horizontal scrolling
55569      */
55570     ensureVisible : function(row, col, hscroll)
55571     {
55572         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
55573         //return null; //disable for testing.
55574         if(typeof row != "number"){
55575             row = row.rowIndex;
55576         }
55577         if(row < 0 && row >= this.ds.getCount()){
55578             return  null;
55579         }
55580         col = (col !== undefined ? col : 0);
55581         var cm = this.grid.colModel;
55582         while(cm.isHidden(col)){
55583             col++;
55584         }
55585
55586         var el = this.getCell(row, col);
55587         if(!el){
55588             return null;
55589         }
55590         var c = this.scroller.dom;
55591
55592         var ctop = parseInt(el.offsetTop, 10);
55593         var cleft = parseInt(el.offsetLeft, 10);
55594         var cbot = ctop + el.offsetHeight;
55595         var cright = cleft + el.offsetWidth;
55596         
55597         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
55598         var stop = parseInt(c.scrollTop, 10);
55599         var sleft = parseInt(c.scrollLeft, 10);
55600         var sbot = stop + ch;
55601         var sright = sleft + c.clientWidth;
55602         /*
55603         Roo.log('GridView.ensureVisible:' +
55604                 ' ctop:' + ctop +
55605                 ' c.clientHeight:' + c.clientHeight +
55606                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
55607                 ' stop:' + stop +
55608                 ' cbot:' + cbot +
55609                 ' sbot:' + sbot +
55610                 ' ch:' + ch  
55611                 );
55612         */
55613         if(ctop < stop){
55614              c.scrollTop = ctop;
55615             //Roo.log("set scrolltop to ctop DISABLE?");
55616         }else if(cbot > sbot){
55617             //Roo.log("set scrolltop to cbot-ch");
55618             c.scrollTop = cbot-ch;
55619         }
55620         
55621         if(hscroll !== false){
55622             if(cleft < sleft){
55623                 c.scrollLeft = cleft;
55624             }else if(cright > sright){
55625                 c.scrollLeft = cright-c.clientWidth;
55626             }
55627         }
55628          
55629         return el;
55630     },
55631
55632     updateColumns : function(){
55633         this.grid.stopEditing();
55634         var cm = this.grid.colModel, colIds = this.getColumnIds();
55635         //var totalWidth = cm.getTotalWidth();
55636         var pos = 0;
55637         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55638             //if(cm.isHidden(i)) continue;
55639             var w = cm.getColumnWidth(i);
55640             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55641             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
55642         }
55643         this.updateSplitters();
55644     },
55645
55646     generateRules : function(cm){
55647         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
55648         Roo.util.CSS.removeStyleSheet(rulesId);
55649         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55650             var cid = cm.getColumnId(i);
55651             var align = '';
55652             if(cm.config[i].align){
55653                 align = 'text-align:'+cm.config[i].align+';';
55654             }
55655             var hidden = '';
55656             if(cm.isHidden(i)){
55657                 hidden = 'display:none;';
55658             }
55659             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
55660             ruleBuf.push(
55661                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
55662                     this.hdSelector, cid, " {\n", align, width, "}\n",
55663                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
55664                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
55665         }
55666         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55667     },
55668
55669     updateSplitters : function(){
55670         var cm = this.cm, s = this.getSplitters();
55671         if(s){ // splitters not created yet
55672             var pos = 0, locked = true;
55673             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55674                 if(cm.isHidden(i)) {
55675                     continue;
55676                 }
55677                 var w = cm.getColumnWidth(i); // make sure it's a number
55678                 if(!cm.isLocked(i) && locked){
55679                     pos = 0;
55680                     locked = false;
55681                 }
55682                 pos += w;
55683                 s[i].style.left = (pos-this.splitOffset) + "px";
55684             }
55685         }
55686     },
55687
55688     handleHiddenChange : function(colModel, colIndex, hidden){
55689         if(hidden){
55690             this.hideColumn(colIndex);
55691         }else{
55692             this.unhideColumn(colIndex);
55693         }
55694     },
55695
55696     hideColumn : function(colIndex){
55697         var cid = this.getColumnId(colIndex);
55698         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
55699         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
55700         if(Roo.isSafari){
55701             this.updateHeaders();
55702         }
55703         this.updateSplitters();
55704         this.layout();
55705     },
55706
55707     unhideColumn : function(colIndex){
55708         var cid = this.getColumnId(colIndex);
55709         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
55710         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
55711
55712         if(Roo.isSafari){
55713             this.updateHeaders();
55714         }
55715         this.updateSplitters();
55716         this.layout();
55717     },
55718
55719     insertRows : function(dm, firstRow, lastRow, isUpdate){
55720         if(firstRow == 0 && lastRow == dm.getCount()-1){
55721             this.refresh();
55722         }else{
55723             if(!isUpdate){
55724                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
55725             }
55726             var s = this.getScrollState();
55727             var markup = this.renderRows(firstRow, lastRow);
55728             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
55729             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
55730             this.restoreScroll(s);
55731             if(!isUpdate){
55732                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
55733                 this.syncRowHeights(firstRow, lastRow);
55734                 this.stripeRows(firstRow);
55735                 this.layout();
55736             }
55737         }
55738     },
55739
55740     bufferRows : function(markup, target, index){
55741         var before = null, trows = target.rows, tbody = target.tBodies[0];
55742         if(index < trows.length){
55743             before = trows[index];
55744         }
55745         var b = document.createElement("div");
55746         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
55747         var rows = b.firstChild.rows;
55748         for(var i = 0, len = rows.length; i < len; i++){
55749             if(before){
55750                 tbody.insertBefore(rows[0], before);
55751             }else{
55752                 tbody.appendChild(rows[0]);
55753             }
55754         }
55755         b.innerHTML = "";
55756         b = null;
55757     },
55758
55759     deleteRows : function(dm, firstRow, lastRow){
55760         if(dm.getRowCount()<1){
55761             this.fireEvent("beforerefresh", this);
55762             this.mainBody.update("");
55763             this.lockedBody.update("");
55764             this.fireEvent("refresh", this);
55765         }else{
55766             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
55767             var bt = this.getBodyTable();
55768             var tbody = bt.firstChild;
55769             var rows = bt.rows;
55770             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
55771                 tbody.removeChild(rows[firstRow]);
55772             }
55773             this.stripeRows(firstRow);
55774             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
55775         }
55776     },
55777
55778     updateRows : function(dataSource, firstRow, lastRow){
55779         var s = this.getScrollState();
55780         this.refresh();
55781         this.restoreScroll(s);
55782     },
55783
55784     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
55785         if(!noRefresh){
55786            this.refresh();
55787         }
55788         this.updateHeaderSortState();
55789     },
55790
55791     getScrollState : function(){
55792         
55793         var sb = this.scroller.dom;
55794         return {left: sb.scrollLeft, top: sb.scrollTop};
55795     },
55796
55797     stripeRows : function(startRow){
55798         if(!this.grid.stripeRows || this.ds.getCount() < 1){
55799             return;
55800         }
55801         startRow = startRow || 0;
55802         var rows = this.getBodyTable().rows;
55803         var lrows = this.getLockedTable().rows;
55804         var cls = ' x-grid-row-alt ';
55805         for(var i = startRow, len = rows.length; i < len; i++){
55806             var row = rows[i], lrow = lrows[i];
55807             var isAlt = ((i+1) % 2 == 0);
55808             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
55809             if(isAlt == hasAlt){
55810                 continue;
55811             }
55812             if(isAlt){
55813                 row.className += " x-grid-row-alt";
55814             }else{
55815                 row.className = row.className.replace("x-grid-row-alt", "");
55816             }
55817             if(lrow){
55818                 lrow.className = row.className;
55819             }
55820         }
55821     },
55822
55823     restoreScroll : function(state){
55824         //Roo.log('GridView.restoreScroll');
55825         var sb = this.scroller.dom;
55826         sb.scrollLeft = state.left;
55827         sb.scrollTop = state.top;
55828         this.syncScroll();
55829     },
55830
55831     syncScroll : function(){
55832         //Roo.log('GridView.syncScroll');
55833         var sb = this.scroller.dom;
55834         var sh = this.mainHd.dom;
55835         var bs = this.mainBody.dom;
55836         var lv = this.lockedBody.dom;
55837         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
55838         lv.scrollTop = bs.scrollTop = sb.scrollTop;
55839     },
55840
55841     handleScroll : function(e){
55842         this.syncScroll();
55843         var sb = this.scroller.dom;
55844         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
55845         e.stopEvent();
55846     },
55847
55848     handleWheel : function(e){
55849         var d = e.getWheelDelta();
55850         this.scroller.dom.scrollTop -= d*22;
55851         // set this here to prevent jumpy scrolling on large tables
55852         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
55853         e.stopEvent();
55854     },
55855
55856     renderRows : function(startRow, endRow){
55857         // pull in all the crap needed to render rows
55858         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
55859         var colCount = cm.getColumnCount();
55860
55861         if(ds.getCount() < 1){
55862             return ["", ""];
55863         }
55864
55865         // build a map for all the columns
55866         var cs = [];
55867         for(var i = 0; i < colCount; i++){
55868             var name = cm.getDataIndex(i);
55869             cs[i] = {
55870                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
55871                 renderer : cm.getRenderer(i),
55872                 id : cm.getColumnId(i),
55873                 locked : cm.isLocked(i),
55874                 has_editor : cm.isCellEditable(i)
55875             };
55876         }
55877
55878         startRow = startRow || 0;
55879         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
55880
55881         // records to render
55882         var rs = ds.getRange(startRow, endRow);
55883
55884         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
55885     },
55886
55887     // As much as I hate to duplicate code, this was branched because FireFox really hates
55888     // [].join("") on strings. The performance difference was substantial enough to
55889     // branch this function
55890     doRender : Roo.isGecko ?
55891             function(cs, rs, ds, startRow, colCount, stripe){
55892                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55893                 // buffers
55894                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55895                 
55896                 var hasListener = this.grid.hasListener('rowclass');
55897                 var rowcfg = {};
55898                 for(var j = 0, len = rs.length; j < len; j++){
55899                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
55900                     for(var i = 0; i < colCount; i++){
55901                         c = cs[i];
55902                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55903                         p.id = c.id;
55904                         p.css = p.attr = "";
55905                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55906                         if(p.value == undefined || p.value === "") {
55907                             p.value = "&#160;";
55908                         }
55909                         if(c.has_editor){
55910                             p.css += ' x-grid-editable-cell';
55911                         }
55912                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
55913                             p.css +=  ' x-grid-dirty-cell';
55914                         }
55915                         var markup = ct.apply(p);
55916                         if(!c.locked){
55917                             cb+= markup;
55918                         }else{
55919                             lcb+= markup;
55920                         }
55921                     }
55922                     var alt = [];
55923                     if(stripe && ((rowIndex+1) % 2 == 0)){
55924                         alt.push("x-grid-row-alt")
55925                     }
55926                     if(r.dirty){
55927                         alt.push(  " x-grid-dirty-row");
55928                     }
55929                     rp.cells = lcb;
55930                     if(this.getRowClass){
55931                         alt.push(this.getRowClass(r, rowIndex));
55932                     }
55933                     if (hasListener) {
55934                         rowcfg = {
55935                              
55936                             record: r,
55937                             rowIndex : rowIndex,
55938                             rowClass : ''
55939                         };
55940                         this.grid.fireEvent('rowclass', this, rowcfg);
55941                         alt.push(rowcfg.rowClass);
55942                     }
55943                     rp.alt = alt.join(" ");
55944                     lbuf+= rt.apply(rp);
55945                     rp.cells = cb;
55946                     buf+=  rt.apply(rp);
55947                 }
55948                 return [lbuf, buf];
55949             } :
55950             function(cs, rs, ds, startRow, colCount, stripe){
55951                 var ts = this.templates, ct = ts.cell, rt = ts.row;
55952                 // buffers
55953                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
55954                 var hasListener = this.grid.hasListener('rowclass');
55955  
55956                 var rowcfg = {};
55957                 for(var j = 0, len = rs.length; j < len; j++){
55958                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
55959                     for(var i = 0; i < colCount; i++){
55960                         c = cs[i];
55961                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
55962                         p.id = c.id;
55963                         p.css = p.attr = "";
55964                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
55965                         if(p.value == undefined || p.value === "") {
55966                             p.value = "&#160;";
55967                         }
55968                         //Roo.log(c);
55969                          if(c.has_editor){
55970                             p.css += ' x-grid-editable-cell';
55971                         }
55972                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
55973                             p.css += ' x-grid-dirty-cell' 
55974                         }
55975                         
55976                         var markup = ct.apply(p);
55977                         if(!c.locked){
55978                             cb[cb.length] = markup;
55979                         }else{
55980                             lcb[lcb.length] = markup;
55981                         }
55982                     }
55983                     var alt = [];
55984                     if(stripe && ((rowIndex+1) % 2 == 0)){
55985                         alt.push( "x-grid-row-alt");
55986                     }
55987                     if(r.dirty){
55988                         alt.push(" x-grid-dirty-row");
55989                     }
55990                     rp.cells = lcb;
55991                     if(this.getRowClass){
55992                         alt.push( this.getRowClass(r, rowIndex));
55993                     }
55994                     if (hasListener) {
55995                         rowcfg = {
55996                              
55997                             record: r,
55998                             rowIndex : rowIndex,
55999                             rowClass : ''
56000                         };
56001                         this.grid.fireEvent('rowclass', this, rowcfg);
56002                         alt.push(rowcfg.rowClass);
56003                     }
56004                     
56005                     rp.alt = alt.join(" ");
56006                     rp.cells = lcb.join("");
56007                     lbuf[lbuf.length] = rt.apply(rp);
56008                     rp.cells = cb.join("");
56009                     buf[buf.length] =  rt.apply(rp);
56010                 }
56011                 return [lbuf.join(""), buf.join("")];
56012             },
56013
56014     renderBody : function(){
56015         var markup = this.renderRows();
56016         var bt = this.templates.body;
56017         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56018     },
56019
56020     /**
56021      * Refreshes the grid
56022      * @param {Boolean} headersToo
56023      */
56024     refresh : function(headersToo){
56025         this.fireEvent("beforerefresh", this);
56026         this.grid.stopEditing();
56027         var result = this.renderBody();
56028         this.lockedBody.update(result[0]);
56029         this.mainBody.update(result[1]);
56030         if(headersToo === true){
56031             this.updateHeaders();
56032             this.updateColumns();
56033             this.updateSplitters();
56034             this.updateHeaderSortState();
56035         }
56036         this.syncRowHeights();
56037         this.layout();
56038         this.fireEvent("refresh", this);
56039     },
56040
56041     handleColumnMove : function(cm, oldIndex, newIndex){
56042         this.indexMap = null;
56043         var s = this.getScrollState();
56044         this.refresh(true);
56045         this.restoreScroll(s);
56046         this.afterMove(newIndex);
56047     },
56048
56049     afterMove : function(colIndex){
56050         if(this.enableMoveAnim && Roo.enableFx){
56051             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56052         }
56053         // if multisort - fix sortOrder, and reload..
56054         if (this.grid.dataSource.multiSort) {
56055             // the we can call sort again..
56056             var dm = this.grid.dataSource;
56057             var cm = this.grid.colModel;
56058             var so = [];
56059             for(var i = 0; i < cm.config.length; i++ ) {
56060                 
56061                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56062                     continue; // dont' bother, it's not in sort list or being set.
56063                 }
56064                 
56065                 so.push(cm.config[i].dataIndex);
56066             };
56067             dm.sortOrder = so;
56068             dm.load(dm.lastOptions);
56069             
56070             
56071         }
56072         
56073     },
56074
56075     updateCell : function(dm, rowIndex, dataIndex){
56076         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56077         if(typeof colIndex == "undefined"){ // not present in grid
56078             return;
56079         }
56080         var cm = this.grid.colModel;
56081         var cell = this.getCell(rowIndex, colIndex);
56082         var cellText = this.getCellText(rowIndex, colIndex);
56083
56084         var p = {
56085             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56086             id : cm.getColumnId(colIndex),
56087             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56088         };
56089         var renderer = cm.getRenderer(colIndex);
56090         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56091         if(typeof val == "undefined" || val === "") {
56092             val = "&#160;";
56093         }
56094         cellText.innerHTML = val;
56095         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56096         this.syncRowHeights(rowIndex, rowIndex);
56097     },
56098
56099     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56100         var maxWidth = 0;
56101         if(this.grid.autoSizeHeaders){
56102             var h = this.getHeaderCellMeasure(colIndex);
56103             maxWidth = Math.max(maxWidth, h.scrollWidth);
56104         }
56105         var tb, index;
56106         if(this.cm.isLocked(colIndex)){
56107             tb = this.getLockedTable();
56108             index = colIndex;
56109         }else{
56110             tb = this.getBodyTable();
56111             index = colIndex - this.cm.getLockedCount();
56112         }
56113         if(tb && tb.rows){
56114             var rows = tb.rows;
56115             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56116             for(var i = 0; i < stopIndex; i++){
56117                 var cell = rows[i].childNodes[index].firstChild;
56118                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56119             }
56120         }
56121         return maxWidth + /*margin for error in IE*/ 5;
56122     },
56123     /**
56124      * Autofit a column to its content.
56125      * @param {Number} colIndex
56126      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56127      */
56128      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56129          if(this.cm.isHidden(colIndex)){
56130              return; // can't calc a hidden column
56131          }
56132         if(forceMinSize){
56133             var cid = this.cm.getColumnId(colIndex);
56134             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56135            if(this.grid.autoSizeHeaders){
56136                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56137            }
56138         }
56139         var newWidth = this.calcColumnWidth(colIndex);
56140         this.cm.setColumnWidth(colIndex,
56141             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56142         if(!suppressEvent){
56143             this.grid.fireEvent("columnresize", colIndex, newWidth);
56144         }
56145     },
56146
56147     /**
56148      * Autofits all columns to their content and then expands to fit any extra space in the grid
56149      */
56150      autoSizeColumns : function(){
56151         var cm = this.grid.colModel;
56152         var colCount = cm.getColumnCount();
56153         for(var i = 0; i < colCount; i++){
56154             this.autoSizeColumn(i, true, true);
56155         }
56156         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
56157             this.fitColumns();
56158         }else{
56159             this.updateColumns();
56160             this.layout();
56161         }
56162     },
56163
56164     /**
56165      * Autofits all columns to the grid's width proportionate with their current size
56166      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
56167      */
56168     fitColumns : function(reserveScrollSpace){
56169         var cm = this.grid.colModel;
56170         var colCount = cm.getColumnCount();
56171         var cols = [];
56172         var width = 0;
56173         var i, w;
56174         for (i = 0; i < colCount; i++){
56175             if(!cm.isHidden(i) && !cm.isFixed(i)){
56176                 w = cm.getColumnWidth(i);
56177                 cols.push(i);
56178                 cols.push(w);
56179                 width += w;
56180             }
56181         }
56182         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
56183         if(reserveScrollSpace){
56184             avail -= 17;
56185         }
56186         var frac = (avail - cm.getTotalWidth())/width;
56187         while (cols.length){
56188             w = cols.pop();
56189             i = cols.pop();
56190             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
56191         }
56192         this.updateColumns();
56193         this.layout();
56194     },
56195
56196     onRowSelect : function(rowIndex){
56197         var row = this.getRowComposite(rowIndex);
56198         row.addClass("x-grid-row-selected");
56199     },
56200
56201     onRowDeselect : function(rowIndex){
56202         var row = this.getRowComposite(rowIndex);
56203         row.removeClass("x-grid-row-selected");
56204     },
56205
56206     onCellSelect : function(row, col){
56207         var cell = this.getCell(row, col);
56208         if(cell){
56209             Roo.fly(cell).addClass("x-grid-cell-selected");
56210         }
56211     },
56212
56213     onCellDeselect : function(row, col){
56214         var cell = this.getCell(row, col);
56215         if(cell){
56216             Roo.fly(cell).removeClass("x-grid-cell-selected");
56217         }
56218     },
56219
56220     updateHeaderSortState : function(){
56221         
56222         // sort state can be single { field: xxx, direction : yyy}
56223         // or   { xxx=>ASC , yyy : DESC ..... }
56224         
56225         var mstate = {};
56226         if (!this.ds.multiSort) { 
56227             var state = this.ds.getSortState();
56228             if(!state){
56229                 return;
56230             }
56231             mstate[state.field] = state.direction;
56232             // FIXME... - this is not used here.. but might be elsewhere..
56233             this.sortState = state;
56234             
56235         } else {
56236             mstate = this.ds.sortToggle;
56237         }
56238         //remove existing sort classes..
56239         
56240         var sc = this.sortClasses;
56241         var hds = this.el.select(this.headerSelector).removeClass(sc);
56242         
56243         for(var f in mstate) {
56244         
56245             var sortColumn = this.cm.findColumnIndex(f);
56246             
56247             if(sortColumn != -1){
56248                 var sortDir = mstate[f];        
56249                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
56250             }
56251         }
56252         
56253          
56254         
56255     },
56256
56257
56258     handleHeaderClick : function(g, index,e){
56259         
56260         Roo.log("header click");
56261         
56262         if (Roo.isTouch) {
56263             // touch events on header are handled by context
56264             this.handleHdCtx(g,index,e);
56265             return;
56266         }
56267         
56268         
56269         if(this.headersDisabled){
56270             return;
56271         }
56272         var dm = g.dataSource, cm = g.colModel;
56273         if(!cm.isSortable(index)){
56274             return;
56275         }
56276         g.stopEditing();
56277         
56278         if (dm.multiSort) {
56279             // update the sortOrder
56280             var so = [];
56281             for(var i = 0; i < cm.config.length; i++ ) {
56282                 
56283                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
56284                     continue; // dont' bother, it's not in sort list or being set.
56285                 }
56286                 
56287                 so.push(cm.config[i].dataIndex);
56288             };
56289             dm.sortOrder = so;
56290         }
56291         
56292         
56293         dm.sort(cm.getDataIndex(index));
56294     },
56295
56296
56297     destroy : function(){
56298         if(this.colMenu){
56299             this.colMenu.removeAll();
56300             Roo.menu.MenuMgr.unregister(this.colMenu);
56301             this.colMenu.getEl().remove();
56302             delete this.colMenu;
56303         }
56304         if(this.hmenu){
56305             this.hmenu.removeAll();
56306             Roo.menu.MenuMgr.unregister(this.hmenu);
56307             this.hmenu.getEl().remove();
56308             delete this.hmenu;
56309         }
56310         if(this.grid.enableColumnMove){
56311             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56312             if(dds){
56313                 for(var dd in dds){
56314                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
56315                         var elid = dds[dd].dragElId;
56316                         dds[dd].unreg();
56317                         Roo.get(elid).remove();
56318                     } else if(dds[dd].config.isTarget){
56319                         dds[dd].proxyTop.remove();
56320                         dds[dd].proxyBottom.remove();
56321                         dds[dd].unreg();
56322                     }
56323                     if(Roo.dd.DDM.locationCache[dd]){
56324                         delete Roo.dd.DDM.locationCache[dd];
56325                     }
56326                 }
56327                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
56328             }
56329         }
56330         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
56331         this.bind(null, null);
56332         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
56333     },
56334
56335     handleLockChange : function(){
56336         this.refresh(true);
56337     },
56338
56339     onDenyColumnLock : function(){
56340
56341     },
56342
56343     onDenyColumnHide : function(){
56344
56345     },
56346
56347     handleHdMenuClick : function(item){
56348         var index = this.hdCtxIndex;
56349         var cm = this.cm, ds = this.ds;
56350         switch(item.id){
56351             case "asc":
56352                 ds.sort(cm.getDataIndex(index), "ASC");
56353                 break;
56354             case "desc":
56355                 ds.sort(cm.getDataIndex(index), "DESC");
56356                 break;
56357             case "lock":
56358                 var lc = cm.getLockedCount();
56359                 if(cm.getColumnCount(true) <= lc+1){
56360                     this.onDenyColumnLock();
56361                     return;
56362                 }
56363                 if(lc != index){
56364                     cm.setLocked(index, true, true);
56365                     cm.moveColumn(index, lc);
56366                     this.grid.fireEvent("columnmove", index, lc);
56367                 }else{
56368                     cm.setLocked(index, true);
56369                 }
56370             break;
56371             case "unlock":
56372                 var lc = cm.getLockedCount();
56373                 if((lc-1) != index){
56374                     cm.setLocked(index, false, true);
56375                     cm.moveColumn(index, lc-1);
56376                     this.grid.fireEvent("columnmove", index, lc-1);
56377                 }else{
56378                     cm.setLocked(index, false);
56379                 }
56380             break;
56381             case 'wider': // used to expand cols on touch..
56382             case 'narrow':
56383                 var cw = cm.getColumnWidth(index);
56384                 cw += (item.id == 'wider' ? 1 : -1) * 50;
56385                 cw = Math.max(0, cw);
56386                 cw = Math.min(cw,4000);
56387                 cm.setColumnWidth(index, cw);
56388                 break;
56389                 
56390             default:
56391                 index = cm.getIndexById(item.id.substr(4));
56392                 if(index != -1){
56393                     if(item.checked && cm.getColumnCount(true) <= 1){
56394                         this.onDenyColumnHide();
56395                         return false;
56396                     }
56397                     cm.setHidden(index, item.checked);
56398                 }
56399         }
56400         return true;
56401     },
56402
56403     beforeColMenuShow : function(){
56404         var cm = this.cm,  colCount = cm.getColumnCount();
56405         this.colMenu.removeAll();
56406         for(var i = 0; i < colCount; i++){
56407             this.colMenu.add(new Roo.menu.CheckItem({
56408                 id: "col-"+cm.getColumnId(i),
56409                 text: cm.getColumnHeader(i),
56410                 checked: !cm.isHidden(i),
56411                 hideOnClick:false
56412             }));
56413         }
56414     },
56415
56416     handleHdCtx : function(g, index, e){
56417         e.stopEvent();
56418         var hd = this.getHeaderCell(index);
56419         this.hdCtxIndex = index;
56420         var ms = this.hmenu.items, cm = this.cm;
56421         ms.get("asc").setDisabled(!cm.isSortable(index));
56422         ms.get("desc").setDisabled(!cm.isSortable(index));
56423         if(this.grid.enableColLock !== false){
56424             ms.get("lock").setDisabled(cm.isLocked(index));
56425             ms.get("unlock").setDisabled(!cm.isLocked(index));
56426         }
56427         this.hmenu.show(hd, "tl-bl");
56428     },
56429
56430     handleHdOver : function(e){
56431         var hd = this.findHeaderCell(e.getTarget());
56432         if(hd && !this.headersDisabled){
56433             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
56434                this.fly(hd).addClass("x-grid-hd-over");
56435             }
56436         }
56437     },
56438
56439     handleHdOut : function(e){
56440         var hd = this.findHeaderCell(e.getTarget());
56441         if(hd){
56442             this.fly(hd).removeClass("x-grid-hd-over");
56443         }
56444     },
56445
56446     handleSplitDblClick : function(e, t){
56447         var i = this.getCellIndex(t);
56448         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
56449             this.autoSizeColumn(i, true);
56450             this.layout();
56451         }
56452     },
56453
56454     render : function(){
56455
56456         var cm = this.cm;
56457         var colCount = cm.getColumnCount();
56458
56459         if(this.grid.monitorWindowResize === true){
56460             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
56461         }
56462         var header = this.renderHeaders();
56463         var body = this.templates.body.apply({rows:""});
56464         var html = this.templates.master.apply({
56465             lockedBody: body,
56466             body: body,
56467             lockedHeader: header[0],
56468             header: header[1]
56469         });
56470
56471         //this.updateColumns();
56472
56473         this.grid.getGridEl().dom.innerHTML = html;
56474
56475         this.initElements();
56476         
56477         // a kludge to fix the random scolling effect in webkit
56478         this.el.on("scroll", function() {
56479             this.el.dom.scrollTop=0; // hopefully not recursive..
56480         },this);
56481
56482         this.scroller.on("scroll", this.handleScroll, this);
56483         this.lockedBody.on("mousewheel", this.handleWheel, this);
56484         this.mainBody.on("mousewheel", this.handleWheel, this);
56485
56486         this.mainHd.on("mouseover", this.handleHdOver, this);
56487         this.mainHd.on("mouseout", this.handleHdOut, this);
56488         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
56489                 {delegate: "."+this.splitClass});
56490
56491         this.lockedHd.on("mouseover", this.handleHdOver, this);
56492         this.lockedHd.on("mouseout", this.handleHdOut, this);
56493         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
56494                 {delegate: "."+this.splitClass});
56495
56496         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
56497             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56498         }
56499
56500         this.updateSplitters();
56501
56502         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
56503             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56504             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
56505         }
56506
56507         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
56508             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
56509             this.hmenu.add(
56510                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
56511                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
56512             );
56513             if(this.grid.enableColLock !== false){
56514                 this.hmenu.add('-',
56515                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
56516                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
56517                 );
56518             }
56519             if (Roo.isTouch) {
56520                  this.hmenu.add('-',
56521                     {id:"wider", text: this.columnsWiderText},
56522                     {id:"narrow", text: this.columnsNarrowText }
56523                 );
56524                 
56525                  
56526             }
56527             
56528             if(this.grid.enableColumnHide !== false){
56529
56530                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
56531                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
56532                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
56533
56534                 this.hmenu.add('-',
56535                     {id:"columns", text: this.columnsText, menu: this.colMenu}
56536                 );
56537             }
56538             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
56539
56540             this.grid.on("headercontextmenu", this.handleHdCtx, this);
56541         }
56542
56543         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
56544             this.dd = new Roo.grid.GridDragZone(this.grid, {
56545                 ddGroup : this.grid.ddGroup || 'GridDD'
56546             });
56547             
56548         }
56549
56550         /*
56551         for(var i = 0; i < colCount; i++){
56552             if(cm.isHidden(i)){
56553                 this.hideColumn(i);
56554             }
56555             if(cm.config[i].align){
56556                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
56557                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
56558             }
56559         }*/
56560         
56561         this.updateHeaderSortState();
56562
56563         this.beforeInitialResize();
56564         this.layout(true);
56565
56566         // two part rendering gives faster view to the user
56567         this.renderPhase2.defer(1, this);
56568     },
56569
56570     renderPhase2 : function(){
56571         // render the rows now
56572         this.refresh();
56573         if(this.grid.autoSizeColumns){
56574             this.autoSizeColumns();
56575         }
56576     },
56577
56578     beforeInitialResize : function(){
56579
56580     },
56581
56582     onColumnSplitterMoved : function(i, w){
56583         this.userResized = true;
56584         var cm = this.grid.colModel;
56585         cm.setColumnWidth(i, w, true);
56586         var cid = cm.getColumnId(i);
56587         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56588         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
56589         this.updateSplitters();
56590         this.layout();
56591         this.grid.fireEvent("columnresize", i, w);
56592     },
56593
56594     syncRowHeights : function(startIndex, endIndex){
56595         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
56596             startIndex = startIndex || 0;
56597             var mrows = this.getBodyTable().rows;
56598             var lrows = this.getLockedTable().rows;
56599             var len = mrows.length-1;
56600             endIndex = Math.min(endIndex || len, len);
56601             for(var i = startIndex; i <= endIndex; i++){
56602                 var m = mrows[i], l = lrows[i];
56603                 var h = Math.max(m.offsetHeight, l.offsetHeight);
56604                 m.style.height = l.style.height = h + "px";
56605             }
56606         }
56607     },
56608
56609     layout : function(initialRender, is2ndPass){
56610         var g = this.grid;
56611         var auto = g.autoHeight;
56612         var scrollOffset = 16;
56613         var c = g.getGridEl(), cm = this.cm,
56614                 expandCol = g.autoExpandColumn,
56615                 gv = this;
56616         //c.beginMeasure();
56617
56618         if(!c.dom.offsetWidth){ // display:none?
56619             if(initialRender){
56620                 this.lockedWrap.show();
56621                 this.mainWrap.show();
56622             }
56623             return;
56624         }
56625
56626         var hasLock = this.cm.isLocked(0);
56627
56628         var tbh = this.headerPanel.getHeight();
56629         var bbh = this.footerPanel.getHeight();
56630
56631         if(auto){
56632             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
56633             var newHeight = ch + c.getBorderWidth("tb");
56634             if(g.maxHeight){
56635                 newHeight = Math.min(g.maxHeight, newHeight);
56636             }
56637             c.setHeight(newHeight);
56638         }
56639
56640         if(g.autoWidth){
56641             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
56642         }
56643
56644         var s = this.scroller;
56645
56646         var csize = c.getSize(true);
56647
56648         this.el.setSize(csize.width, csize.height);
56649
56650         this.headerPanel.setWidth(csize.width);
56651         this.footerPanel.setWidth(csize.width);
56652
56653         var hdHeight = this.mainHd.getHeight();
56654         var vw = csize.width;
56655         var vh = csize.height - (tbh + bbh);
56656
56657         s.setSize(vw, vh);
56658
56659         var bt = this.getBodyTable();
56660         
56661         if(cm.getLockedCount() == cm.config.length){
56662             bt = this.getLockedTable();
56663         }
56664         
56665         var ltWidth = hasLock ?
56666                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
56667
56668         var scrollHeight = bt.offsetHeight;
56669         var scrollWidth = ltWidth + bt.offsetWidth;
56670         var vscroll = false, hscroll = false;
56671
56672         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
56673
56674         var lw = this.lockedWrap, mw = this.mainWrap;
56675         var lb = this.lockedBody, mb = this.mainBody;
56676
56677         setTimeout(function(){
56678             var t = s.dom.offsetTop;
56679             var w = s.dom.clientWidth,
56680                 h = s.dom.clientHeight;
56681
56682             lw.setTop(t);
56683             lw.setSize(ltWidth, h);
56684
56685             mw.setLeftTop(ltWidth, t);
56686             mw.setSize(w-ltWidth, h);
56687
56688             lb.setHeight(h-hdHeight);
56689             mb.setHeight(h-hdHeight);
56690
56691             if(is2ndPass !== true && !gv.userResized && expandCol){
56692                 // high speed resize without full column calculation
56693                 
56694                 var ci = cm.getIndexById(expandCol);
56695                 if (ci < 0) {
56696                     ci = cm.findColumnIndex(expandCol);
56697                 }
56698                 ci = Math.max(0, ci); // make sure it's got at least the first col.
56699                 var expandId = cm.getColumnId(ci);
56700                 var  tw = cm.getTotalWidth(false);
56701                 var currentWidth = cm.getColumnWidth(ci);
56702                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
56703                 if(currentWidth != cw){
56704                     cm.setColumnWidth(ci, cw, true);
56705                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56706                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
56707                     gv.updateSplitters();
56708                     gv.layout(false, true);
56709                 }
56710             }
56711
56712             if(initialRender){
56713                 lw.show();
56714                 mw.show();
56715             }
56716             //c.endMeasure();
56717         }, 10);
56718     },
56719
56720     onWindowResize : function(){
56721         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
56722             return;
56723         }
56724         this.layout();
56725     },
56726
56727     appendFooter : function(parentEl){
56728         return null;
56729     },
56730
56731     sortAscText : "Sort Ascending",
56732     sortDescText : "Sort Descending",
56733     lockText : "Lock Column",
56734     unlockText : "Unlock Column",
56735     columnsText : "Columns",
56736  
56737     columnsWiderText : "Wider",
56738     columnsNarrowText : "Thinner"
56739 });
56740
56741
56742 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
56743     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
56744     this.proxy.el.addClass('x-grid3-col-dd');
56745 };
56746
56747 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
56748     handleMouseDown : function(e){
56749
56750     },
56751
56752     callHandleMouseDown : function(e){
56753         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
56754     }
56755 });
56756 /*
56757  * Based on:
56758  * Ext JS Library 1.1.1
56759  * Copyright(c) 2006-2007, Ext JS, LLC.
56760  *
56761  * Originally Released Under LGPL - original licence link has changed is not relivant.
56762  *
56763  * Fork - LGPL
56764  * <script type="text/javascript">
56765  */
56766  
56767 // private
56768 // This is a support class used internally by the Grid components
56769 Roo.grid.SplitDragZone = function(grid, hd, hd2){
56770     this.grid = grid;
56771     this.view = grid.getView();
56772     this.proxy = this.view.resizeProxy;
56773     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
56774         "gridSplitters" + this.grid.getGridEl().id, {
56775         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
56776     });
56777     this.setHandleElId(Roo.id(hd));
56778     this.setOuterHandleElId(Roo.id(hd2));
56779     this.scroll = false;
56780 };
56781 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
56782     fly: Roo.Element.fly,
56783
56784     b4StartDrag : function(x, y){
56785         this.view.headersDisabled = true;
56786         this.proxy.setHeight(this.view.mainWrap.getHeight());
56787         var w = this.cm.getColumnWidth(this.cellIndex);
56788         var minw = Math.max(w-this.grid.minColumnWidth, 0);
56789         this.resetConstraints();
56790         this.setXConstraint(minw, 1000);
56791         this.setYConstraint(0, 0);
56792         this.minX = x - minw;
56793         this.maxX = x + 1000;
56794         this.startPos = x;
56795         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
56796     },
56797
56798
56799     handleMouseDown : function(e){
56800         ev = Roo.EventObject.setEvent(e);
56801         var t = this.fly(ev.getTarget());
56802         if(t.hasClass("x-grid-split")){
56803             this.cellIndex = this.view.getCellIndex(t.dom);
56804             this.split = t.dom;
56805             this.cm = this.grid.colModel;
56806             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
56807                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
56808             }
56809         }
56810     },
56811
56812     endDrag : function(e){
56813         this.view.headersDisabled = false;
56814         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
56815         var diff = endX - this.startPos;
56816         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
56817     },
56818
56819     autoOffset : function(){
56820         this.setDelta(0,0);
56821     }
56822 });/*
56823  * Based on:
56824  * Ext JS Library 1.1.1
56825  * Copyright(c) 2006-2007, Ext JS, LLC.
56826  *
56827  * Originally Released Under LGPL - original licence link has changed is not relivant.
56828  *
56829  * Fork - LGPL
56830  * <script type="text/javascript">
56831  */
56832  
56833 // private
56834 // This is a support class used internally by the Grid components
56835 Roo.grid.GridDragZone = function(grid, config){
56836     this.view = grid.getView();
56837     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
56838     if(this.view.lockedBody){
56839         this.setHandleElId(Roo.id(this.view.mainBody.dom));
56840         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
56841     }
56842     this.scroll = false;
56843     this.grid = grid;
56844     this.ddel = document.createElement('div');
56845     this.ddel.className = 'x-grid-dd-wrap';
56846 };
56847
56848 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
56849     ddGroup : "GridDD",
56850
56851     getDragData : function(e){
56852         var t = Roo.lib.Event.getTarget(e);
56853         var rowIndex = this.view.findRowIndex(t);
56854         var sm = this.grid.selModel;
56855             
56856         //Roo.log(rowIndex);
56857         
56858         if (sm.getSelectedCell) {
56859             // cell selection..
56860             if (!sm.getSelectedCell()) {
56861                 return false;
56862             }
56863             if (rowIndex != sm.getSelectedCell()[0]) {
56864                 return false;
56865             }
56866         
56867         }
56868         
56869         if(rowIndex !== false){
56870             
56871             // if editorgrid.. 
56872             
56873             
56874             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
56875                
56876             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
56877               //  
56878             //}
56879             if (e.hasModifier()){
56880                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
56881             }
56882             
56883             Roo.log("getDragData");
56884             
56885             return {
56886                 grid: this.grid,
56887                 ddel: this.ddel,
56888                 rowIndex: rowIndex,
56889                 selections:sm.getSelections ? sm.getSelections() : (
56890                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
56891                 )
56892             };
56893         }
56894         return false;
56895     },
56896
56897     onInitDrag : function(e){
56898         var data = this.dragData;
56899         this.ddel.innerHTML = this.grid.getDragDropText();
56900         this.proxy.update(this.ddel);
56901         // fire start drag?
56902     },
56903
56904     afterRepair : function(){
56905         this.dragging = false;
56906     },
56907
56908     getRepairXY : function(e, data){
56909         return false;
56910     },
56911
56912     onEndDrag : function(data, e){
56913         // fire end drag?
56914     },
56915
56916     onValidDrop : function(dd, e, id){
56917         // fire drag drop?
56918         this.hideProxy();
56919     },
56920
56921     beforeInvalidDrop : function(e, id){
56922
56923     }
56924 });/*
56925  * Based on:
56926  * Ext JS Library 1.1.1
56927  * Copyright(c) 2006-2007, Ext JS, LLC.
56928  *
56929  * Originally Released Under LGPL - original licence link has changed is not relivant.
56930  *
56931  * Fork - LGPL
56932  * <script type="text/javascript">
56933  */
56934  
56935
56936 /**
56937  * @class Roo.grid.ColumnModel
56938  * @extends Roo.util.Observable
56939  * This is the default implementation of a ColumnModel used by the Grid. It defines
56940  * the columns in the grid.
56941  * <br>Usage:<br>
56942  <pre><code>
56943  var colModel = new Roo.grid.ColumnModel([
56944         {header: "Ticker", width: 60, sortable: true, locked: true},
56945         {header: "Company Name", width: 150, sortable: true},
56946         {header: "Market Cap.", width: 100, sortable: true},
56947         {header: "$ Sales", width: 100, sortable: true, renderer: money},
56948         {header: "Employees", width: 100, sortable: true, resizable: false}
56949  ]);
56950  </code></pre>
56951  * <p>
56952  
56953  * The config options listed for this class are options which may appear in each
56954  * individual column definition.
56955  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
56956  * @constructor
56957  * @param {Object} config An Array of column config objects. See this class's
56958  * config objects for details.
56959 */
56960 Roo.grid.ColumnModel = function(config){
56961         /**
56962      * The config passed into the constructor
56963      */
56964     this.config = config;
56965     this.lookup = {};
56966
56967     // if no id, create one
56968     // if the column does not have a dataIndex mapping,
56969     // map it to the order it is in the config
56970     for(var i = 0, len = config.length; i < len; i++){
56971         var c = config[i];
56972         if(typeof c.dataIndex == "undefined"){
56973             c.dataIndex = i;
56974         }
56975         if(typeof c.renderer == "string"){
56976             c.renderer = Roo.util.Format[c.renderer];
56977         }
56978         if(typeof c.id == "undefined"){
56979             c.id = Roo.id();
56980         }
56981         if(c.editor && c.editor.xtype){
56982             c.editor  = Roo.factory(c.editor, Roo.grid);
56983         }
56984         if(c.editor && c.editor.isFormField){
56985             c.editor = new Roo.grid.GridEditor(c.editor);
56986         }
56987         this.lookup[c.id] = c;
56988     }
56989
56990     /**
56991      * The width of columns which have no width specified (defaults to 100)
56992      * @type Number
56993      */
56994     this.defaultWidth = 100;
56995
56996     /**
56997      * Default sortable of columns which have no sortable specified (defaults to false)
56998      * @type Boolean
56999      */
57000     this.defaultSortable = false;
57001
57002     this.addEvents({
57003         /**
57004              * @event widthchange
57005              * Fires when the width of a column changes.
57006              * @param {ColumnModel} this
57007              * @param {Number} columnIndex The column index
57008              * @param {Number} newWidth The new width
57009              */
57010             "widthchange": true,
57011         /**
57012              * @event headerchange
57013              * Fires when the text of a header changes.
57014              * @param {ColumnModel} this
57015              * @param {Number} columnIndex The column index
57016              * @param {Number} newText The new header text
57017              */
57018             "headerchange": true,
57019         /**
57020              * @event hiddenchange
57021              * Fires when a column is hidden or "unhidden".
57022              * @param {ColumnModel} this
57023              * @param {Number} columnIndex The column index
57024              * @param {Boolean} hidden true if hidden, false otherwise
57025              */
57026             "hiddenchange": true,
57027             /**
57028          * @event columnmoved
57029          * Fires when a column is moved.
57030          * @param {ColumnModel} this
57031          * @param {Number} oldIndex
57032          * @param {Number} newIndex
57033          */
57034         "columnmoved" : true,
57035         /**
57036          * @event columlockchange
57037          * Fires when a column's locked state is changed
57038          * @param {ColumnModel} this
57039          * @param {Number} colIndex
57040          * @param {Boolean} locked true if locked
57041          */
57042         "columnlockchange" : true
57043     });
57044     Roo.grid.ColumnModel.superclass.constructor.call(this);
57045 };
57046 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57047     /**
57048      * @cfg {String} header The header text to display in the Grid view.
57049      */
57050     /**
57051      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57052      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57053      * specified, the column's index is used as an index into the Record's data Array.
57054      */
57055     /**
57056      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57057      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57058      */
57059     /**
57060      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57061      * Defaults to the value of the {@link #defaultSortable} property.
57062      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57063      */
57064     /**
57065      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57066      */
57067     /**
57068      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57069      */
57070     /**
57071      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57072      */
57073     /**
57074      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57075      */
57076     /**
57077      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57078      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57079      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57080      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57081      */
57082        /**
57083      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57084      */
57085     /**
57086      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57087      */
57088     /**
57089      * @cfg {String} cursor (Optional)
57090      */
57091     /**
57092      * @cfg {String} tooltip (Optional)
57093      */
57094     /**
57095      * @cfg {Number} xs (Optional)
57096      */
57097     /**
57098      * @cfg {Number} sm (Optional)
57099      */
57100     /**
57101      * @cfg {Number} md (Optional)
57102      */
57103     /**
57104      * @cfg {Number} lg (Optional)
57105      */
57106     /**
57107      * Returns the id of the column at the specified index.
57108      * @param {Number} index The column index
57109      * @return {String} the id
57110      */
57111     getColumnId : function(index){
57112         return this.config[index].id;
57113     },
57114
57115     /**
57116      * Returns the column for a specified id.
57117      * @param {String} id The column id
57118      * @return {Object} the column
57119      */
57120     getColumnById : function(id){
57121         return this.lookup[id];
57122     },
57123
57124     
57125     /**
57126      * Returns the column for a specified dataIndex.
57127      * @param {String} dataIndex The column dataIndex
57128      * @return {Object|Boolean} the column or false if not found
57129      */
57130     getColumnByDataIndex: function(dataIndex){
57131         var index = this.findColumnIndex(dataIndex);
57132         return index > -1 ? this.config[index] : false;
57133     },
57134     
57135     /**
57136      * Returns the index for a specified column id.
57137      * @param {String} id The column id
57138      * @return {Number} the index, or -1 if not found
57139      */
57140     getIndexById : function(id){
57141         for(var i = 0, len = this.config.length; i < len; i++){
57142             if(this.config[i].id == id){
57143                 return i;
57144             }
57145         }
57146         return -1;
57147     },
57148     
57149     /**
57150      * Returns the index for a specified column dataIndex.
57151      * @param {String} dataIndex The column dataIndex
57152      * @return {Number} the index, or -1 if not found
57153      */
57154     
57155     findColumnIndex : function(dataIndex){
57156         for(var i = 0, len = this.config.length; i < len; i++){
57157             if(this.config[i].dataIndex == dataIndex){
57158                 return i;
57159             }
57160         }
57161         return -1;
57162     },
57163     
57164     
57165     moveColumn : function(oldIndex, newIndex){
57166         var c = this.config[oldIndex];
57167         this.config.splice(oldIndex, 1);
57168         this.config.splice(newIndex, 0, c);
57169         this.dataMap = null;
57170         this.fireEvent("columnmoved", this, oldIndex, newIndex);
57171     },
57172
57173     isLocked : function(colIndex){
57174         return this.config[colIndex].locked === true;
57175     },
57176
57177     setLocked : function(colIndex, value, suppressEvent){
57178         if(this.isLocked(colIndex) == value){
57179             return;
57180         }
57181         this.config[colIndex].locked = value;
57182         if(!suppressEvent){
57183             this.fireEvent("columnlockchange", this, colIndex, value);
57184         }
57185     },
57186
57187     getTotalLockedWidth : function(){
57188         var totalWidth = 0;
57189         for(var i = 0; i < this.config.length; i++){
57190             if(this.isLocked(i) && !this.isHidden(i)){
57191                 this.totalWidth += this.getColumnWidth(i);
57192             }
57193         }
57194         return totalWidth;
57195     },
57196
57197     getLockedCount : function(){
57198         for(var i = 0, len = this.config.length; i < len; i++){
57199             if(!this.isLocked(i)){
57200                 return i;
57201             }
57202         }
57203         
57204         return this.config.length;
57205     },
57206
57207     /**
57208      * Returns the number of columns.
57209      * @return {Number}
57210      */
57211     getColumnCount : function(visibleOnly){
57212         if(visibleOnly === true){
57213             var c = 0;
57214             for(var i = 0, len = this.config.length; i < len; i++){
57215                 if(!this.isHidden(i)){
57216                     c++;
57217                 }
57218             }
57219             return c;
57220         }
57221         return this.config.length;
57222     },
57223
57224     /**
57225      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
57226      * @param {Function} fn
57227      * @param {Object} scope (optional)
57228      * @return {Array} result
57229      */
57230     getColumnsBy : function(fn, scope){
57231         var r = [];
57232         for(var i = 0, len = this.config.length; i < len; i++){
57233             var c = this.config[i];
57234             if(fn.call(scope||this, c, i) === true){
57235                 r[r.length] = c;
57236             }
57237         }
57238         return r;
57239     },
57240
57241     /**
57242      * Returns true if the specified column is sortable.
57243      * @param {Number} col The column index
57244      * @return {Boolean}
57245      */
57246     isSortable : function(col){
57247         if(typeof this.config[col].sortable == "undefined"){
57248             return this.defaultSortable;
57249         }
57250         return this.config[col].sortable;
57251     },
57252
57253     /**
57254      * Returns the rendering (formatting) function defined for the column.
57255      * @param {Number} col The column index.
57256      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
57257      */
57258     getRenderer : function(col){
57259         if(!this.config[col].renderer){
57260             return Roo.grid.ColumnModel.defaultRenderer;
57261         }
57262         return this.config[col].renderer;
57263     },
57264
57265     /**
57266      * Sets the rendering (formatting) function for a column.
57267      * @param {Number} col The column index
57268      * @param {Function} fn The function to use to process the cell's raw data
57269      * to return HTML markup for the grid view. The render function is called with
57270      * the following parameters:<ul>
57271      * <li>Data value.</li>
57272      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
57273      * <li>css A CSS style string to apply to the table cell.</li>
57274      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
57275      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
57276      * <li>Row index</li>
57277      * <li>Column index</li>
57278      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
57279      */
57280     setRenderer : function(col, fn){
57281         this.config[col].renderer = fn;
57282     },
57283
57284     /**
57285      * Returns the width for the specified column.
57286      * @param {Number} col The column index
57287      * @return {Number}
57288      */
57289     getColumnWidth : function(col){
57290         return this.config[col].width * 1 || this.defaultWidth;
57291     },
57292
57293     /**
57294      * Sets the width for a column.
57295      * @param {Number} col The column index
57296      * @param {Number} width The new width
57297      */
57298     setColumnWidth : function(col, width, suppressEvent){
57299         this.config[col].width = width;
57300         this.totalWidth = null;
57301         if(!suppressEvent){
57302              this.fireEvent("widthchange", this, col, width);
57303         }
57304     },
57305
57306     /**
57307      * Returns the total width of all columns.
57308      * @param {Boolean} includeHidden True to include hidden column widths
57309      * @return {Number}
57310      */
57311     getTotalWidth : function(includeHidden){
57312         if(!this.totalWidth){
57313             this.totalWidth = 0;
57314             for(var i = 0, len = this.config.length; i < len; i++){
57315                 if(includeHidden || !this.isHidden(i)){
57316                     this.totalWidth += this.getColumnWidth(i);
57317                 }
57318             }
57319         }
57320         return this.totalWidth;
57321     },
57322
57323     /**
57324      * Returns the header for the specified column.
57325      * @param {Number} col The column index
57326      * @return {String}
57327      */
57328     getColumnHeader : function(col){
57329         return this.config[col].header;
57330     },
57331
57332     /**
57333      * Sets the header for a column.
57334      * @param {Number} col The column index
57335      * @param {String} header The new header
57336      */
57337     setColumnHeader : function(col, header){
57338         this.config[col].header = header;
57339         this.fireEvent("headerchange", this, col, header);
57340     },
57341
57342     /**
57343      * Returns the tooltip for the specified column.
57344      * @param {Number} col The column index
57345      * @return {String}
57346      */
57347     getColumnTooltip : function(col){
57348             return this.config[col].tooltip;
57349     },
57350     /**
57351      * Sets the tooltip for a column.
57352      * @param {Number} col The column index
57353      * @param {String} tooltip The new tooltip
57354      */
57355     setColumnTooltip : function(col, tooltip){
57356             this.config[col].tooltip = tooltip;
57357     },
57358
57359     /**
57360      * Returns the dataIndex for the specified column.
57361      * @param {Number} col The column index
57362      * @return {Number}
57363      */
57364     getDataIndex : function(col){
57365         return this.config[col].dataIndex;
57366     },
57367
57368     /**
57369      * Sets the dataIndex for a column.
57370      * @param {Number} col The column index
57371      * @param {Number} dataIndex The new dataIndex
57372      */
57373     setDataIndex : function(col, dataIndex){
57374         this.config[col].dataIndex = dataIndex;
57375     },
57376
57377     
57378     
57379     /**
57380      * Returns true if the cell is editable.
57381      * @param {Number} colIndex The column index
57382      * @param {Number} rowIndex The row index - this is nto actually used..?
57383      * @return {Boolean}
57384      */
57385     isCellEditable : function(colIndex, rowIndex){
57386         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
57387     },
57388
57389     /**
57390      * Returns the editor defined for the cell/column.
57391      * return false or null to disable editing.
57392      * @param {Number} colIndex The column index
57393      * @param {Number} rowIndex The row index
57394      * @return {Object}
57395      */
57396     getCellEditor : function(colIndex, rowIndex){
57397         return this.config[colIndex].editor;
57398     },
57399
57400     /**
57401      * Sets if a column is editable.
57402      * @param {Number} col The column index
57403      * @param {Boolean} editable True if the column is editable
57404      */
57405     setEditable : function(col, editable){
57406         this.config[col].editable = editable;
57407     },
57408
57409
57410     /**
57411      * Returns true if the column is hidden.
57412      * @param {Number} colIndex The column index
57413      * @return {Boolean}
57414      */
57415     isHidden : function(colIndex){
57416         return this.config[colIndex].hidden;
57417     },
57418
57419
57420     /**
57421      * Returns true if the column width cannot be changed
57422      */
57423     isFixed : function(colIndex){
57424         return this.config[colIndex].fixed;
57425     },
57426
57427     /**
57428      * Returns true if the column can be resized
57429      * @return {Boolean}
57430      */
57431     isResizable : function(colIndex){
57432         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
57433     },
57434     /**
57435      * Sets if a column is hidden.
57436      * @param {Number} colIndex The column index
57437      * @param {Boolean} hidden True if the column is hidden
57438      */
57439     setHidden : function(colIndex, hidden){
57440         this.config[colIndex].hidden = hidden;
57441         this.totalWidth = null;
57442         this.fireEvent("hiddenchange", this, colIndex, hidden);
57443     },
57444
57445     /**
57446      * Sets the editor for a column.
57447      * @param {Number} col The column index
57448      * @param {Object} editor The editor object
57449      */
57450     setEditor : function(col, editor){
57451         this.config[col].editor = editor;
57452     }
57453 });
57454
57455 Roo.grid.ColumnModel.defaultRenderer = function(value)
57456 {
57457     if(typeof value == "object") {
57458         return value;
57459     }
57460         if(typeof value == "string" && value.length < 1){
57461             return "&#160;";
57462         }
57463     
57464         return String.format("{0}", value);
57465 };
57466
57467 // Alias for backwards compatibility
57468 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
57469 /*
57470  * Based on:
57471  * Ext JS Library 1.1.1
57472  * Copyright(c) 2006-2007, Ext JS, LLC.
57473  *
57474  * Originally Released Under LGPL - original licence link has changed is not relivant.
57475  *
57476  * Fork - LGPL
57477  * <script type="text/javascript">
57478  */
57479
57480 /**
57481  * @class Roo.grid.AbstractSelectionModel
57482  * @extends Roo.util.Observable
57483  * Abstract base class for grid SelectionModels.  It provides the interface that should be
57484  * implemented by descendant classes.  This class should not be directly instantiated.
57485  * @constructor
57486  */
57487 Roo.grid.AbstractSelectionModel = function(){
57488     this.locked = false;
57489     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
57490 };
57491
57492 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
57493     /** @ignore Called by the grid automatically. Do not call directly. */
57494     init : function(grid){
57495         this.grid = grid;
57496         this.initEvents();
57497     },
57498
57499     /**
57500      * Locks the selections.
57501      */
57502     lock : function(){
57503         this.locked = true;
57504     },
57505
57506     /**
57507      * Unlocks the selections.
57508      */
57509     unlock : function(){
57510         this.locked = false;
57511     },
57512
57513     /**
57514      * Returns true if the selections are locked.
57515      * @return {Boolean}
57516      */
57517     isLocked : function(){
57518         return this.locked;
57519     }
57520 });/*
57521  * Based on:
57522  * Ext JS Library 1.1.1
57523  * Copyright(c) 2006-2007, Ext JS, LLC.
57524  *
57525  * Originally Released Under LGPL - original licence link has changed is not relivant.
57526  *
57527  * Fork - LGPL
57528  * <script type="text/javascript">
57529  */
57530 /**
57531  * @extends Roo.grid.AbstractSelectionModel
57532  * @class Roo.grid.RowSelectionModel
57533  * The default SelectionModel used by {@link Roo.grid.Grid}.
57534  * It supports multiple selections and keyboard selection/navigation. 
57535  * @constructor
57536  * @param {Object} config
57537  */
57538 Roo.grid.RowSelectionModel = function(config){
57539     Roo.apply(this, config);
57540     this.selections = new Roo.util.MixedCollection(false, function(o){
57541         return o.id;
57542     });
57543
57544     this.last = false;
57545     this.lastActive = false;
57546
57547     this.addEvents({
57548         /**
57549              * @event selectionchange
57550              * Fires when the selection changes
57551              * @param {SelectionModel} this
57552              */
57553             "selectionchange" : true,
57554         /**
57555              * @event afterselectionchange
57556              * Fires after the selection changes (eg. by key press or clicking)
57557              * @param {SelectionModel} this
57558              */
57559             "afterselectionchange" : true,
57560         /**
57561              * @event beforerowselect
57562              * Fires when a row is selected being selected, return false to cancel.
57563              * @param {SelectionModel} this
57564              * @param {Number} rowIndex The selected index
57565              * @param {Boolean} keepExisting False if other selections will be cleared
57566              */
57567             "beforerowselect" : true,
57568         /**
57569              * @event rowselect
57570              * Fires when a row is selected.
57571              * @param {SelectionModel} this
57572              * @param {Number} rowIndex The selected index
57573              * @param {Roo.data.Record} r The record
57574              */
57575             "rowselect" : true,
57576         /**
57577              * @event rowdeselect
57578              * Fires when a row is deselected.
57579              * @param {SelectionModel} this
57580              * @param {Number} rowIndex The selected index
57581              */
57582         "rowdeselect" : true
57583     });
57584     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
57585     this.locked = false;
57586 };
57587
57588 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
57589     /**
57590      * @cfg {Boolean} singleSelect
57591      * True to allow selection of only one row at a time (defaults to false)
57592      */
57593     singleSelect : false,
57594
57595     // private
57596     initEvents : function(){
57597
57598         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
57599             this.grid.on("mousedown", this.handleMouseDown, this);
57600         }else{ // allow click to work like normal
57601             this.grid.on("rowclick", this.handleDragableRowClick, this);
57602         }
57603
57604         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
57605             "up" : function(e){
57606                 if(!e.shiftKey){
57607                     this.selectPrevious(e.shiftKey);
57608                 }else if(this.last !== false && this.lastActive !== false){
57609                     var last = this.last;
57610                     this.selectRange(this.last,  this.lastActive-1);
57611                     this.grid.getView().focusRow(this.lastActive);
57612                     if(last !== false){
57613                         this.last = last;
57614                     }
57615                 }else{
57616                     this.selectFirstRow();
57617                 }
57618                 this.fireEvent("afterselectionchange", this);
57619             },
57620             "down" : function(e){
57621                 if(!e.shiftKey){
57622                     this.selectNext(e.shiftKey);
57623                 }else if(this.last !== false && this.lastActive !== false){
57624                     var last = this.last;
57625                     this.selectRange(this.last,  this.lastActive+1);
57626                     this.grid.getView().focusRow(this.lastActive);
57627                     if(last !== false){
57628                         this.last = last;
57629                     }
57630                 }else{
57631                     this.selectFirstRow();
57632                 }
57633                 this.fireEvent("afterselectionchange", this);
57634             },
57635             scope: this
57636         });
57637
57638         var view = this.grid.view;
57639         view.on("refresh", this.onRefresh, this);
57640         view.on("rowupdated", this.onRowUpdated, this);
57641         view.on("rowremoved", this.onRemove, this);
57642     },
57643
57644     // private
57645     onRefresh : function(){
57646         var ds = this.grid.dataSource, i, v = this.grid.view;
57647         var s = this.selections;
57648         s.each(function(r){
57649             if((i = ds.indexOfId(r.id)) != -1){
57650                 v.onRowSelect(i);
57651                 s.add(ds.getAt(i)); // updating the selection relate data
57652             }else{
57653                 s.remove(r);
57654             }
57655         });
57656     },
57657
57658     // private
57659     onRemove : function(v, index, r){
57660         this.selections.remove(r);
57661     },
57662
57663     // private
57664     onRowUpdated : function(v, index, r){
57665         if(this.isSelected(r)){
57666             v.onRowSelect(index);
57667         }
57668     },
57669
57670     /**
57671      * Select records.
57672      * @param {Array} records The records to select
57673      * @param {Boolean} keepExisting (optional) True to keep existing selections
57674      */
57675     selectRecords : function(records, keepExisting){
57676         if(!keepExisting){
57677             this.clearSelections();
57678         }
57679         var ds = this.grid.dataSource;
57680         for(var i = 0, len = records.length; i < len; i++){
57681             this.selectRow(ds.indexOf(records[i]), true);
57682         }
57683     },
57684
57685     /**
57686      * Gets the number of selected rows.
57687      * @return {Number}
57688      */
57689     getCount : function(){
57690         return this.selections.length;
57691     },
57692
57693     /**
57694      * Selects the first row in the grid.
57695      */
57696     selectFirstRow : function(){
57697         this.selectRow(0);
57698     },
57699
57700     /**
57701      * Select the last row.
57702      * @param {Boolean} keepExisting (optional) True to keep existing selections
57703      */
57704     selectLastRow : function(keepExisting){
57705         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
57706     },
57707
57708     /**
57709      * Selects the row immediately following the last selected row.
57710      * @param {Boolean} keepExisting (optional) True to keep existing selections
57711      */
57712     selectNext : function(keepExisting){
57713         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
57714             this.selectRow(this.last+1, keepExisting);
57715             this.grid.getView().focusRow(this.last);
57716         }
57717     },
57718
57719     /**
57720      * Selects the row that precedes the last selected row.
57721      * @param {Boolean} keepExisting (optional) True to keep existing selections
57722      */
57723     selectPrevious : function(keepExisting){
57724         if(this.last){
57725             this.selectRow(this.last-1, keepExisting);
57726             this.grid.getView().focusRow(this.last);
57727         }
57728     },
57729
57730     /**
57731      * Returns the selected records
57732      * @return {Array} Array of selected records
57733      */
57734     getSelections : function(){
57735         return [].concat(this.selections.items);
57736     },
57737
57738     /**
57739      * Returns the first selected record.
57740      * @return {Record}
57741      */
57742     getSelected : function(){
57743         return this.selections.itemAt(0);
57744     },
57745
57746
57747     /**
57748      * Clears all selections.
57749      */
57750     clearSelections : function(fast){
57751         if(this.locked) {
57752             return;
57753         }
57754         if(fast !== true){
57755             var ds = this.grid.dataSource;
57756             var s = this.selections;
57757             s.each(function(r){
57758                 this.deselectRow(ds.indexOfId(r.id));
57759             }, this);
57760             s.clear();
57761         }else{
57762             this.selections.clear();
57763         }
57764         this.last = false;
57765     },
57766
57767
57768     /**
57769      * Selects all rows.
57770      */
57771     selectAll : function(){
57772         if(this.locked) {
57773             return;
57774         }
57775         this.selections.clear();
57776         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
57777             this.selectRow(i, true);
57778         }
57779     },
57780
57781     /**
57782      * Returns True if there is a selection.
57783      * @return {Boolean}
57784      */
57785     hasSelection : function(){
57786         return this.selections.length > 0;
57787     },
57788
57789     /**
57790      * Returns True if the specified row is selected.
57791      * @param {Number/Record} record The record or index of the record to check
57792      * @return {Boolean}
57793      */
57794     isSelected : function(index){
57795         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
57796         return (r && this.selections.key(r.id) ? true : false);
57797     },
57798
57799     /**
57800      * Returns True if the specified record id is selected.
57801      * @param {String} id The id of record to check
57802      * @return {Boolean}
57803      */
57804     isIdSelected : function(id){
57805         return (this.selections.key(id) ? true : false);
57806     },
57807
57808     // private
57809     handleMouseDown : function(e, t){
57810         var view = this.grid.getView(), rowIndex;
57811         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
57812             return;
57813         };
57814         if(e.shiftKey && this.last !== false){
57815             var last = this.last;
57816             this.selectRange(last, rowIndex, e.ctrlKey);
57817             this.last = last; // reset the last
57818             view.focusRow(rowIndex);
57819         }else{
57820             var isSelected = this.isSelected(rowIndex);
57821             if(e.button !== 0 && isSelected){
57822                 view.focusRow(rowIndex);
57823             }else if(e.ctrlKey && isSelected){
57824                 this.deselectRow(rowIndex);
57825             }else if(!isSelected){
57826                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
57827                 view.focusRow(rowIndex);
57828             }
57829         }
57830         this.fireEvent("afterselectionchange", this);
57831     },
57832     // private
57833     handleDragableRowClick :  function(grid, rowIndex, e) 
57834     {
57835         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
57836             this.selectRow(rowIndex, false);
57837             grid.view.focusRow(rowIndex);
57838              this.fireEvent("afterselectionchange", this);
57839         }
57840     },
57841     
57842     /**
57843      * Selects multiple rows.
57844      * @param {Array} rows Array of the indexes of the row to select
57845      * @param {Boolean} keepExisting (optional) True to keep existing selections
57846      */
57847     selectRows : function(rows, keepExisting){
57848         if(!keepExisting){
57849             this.clearSelections();
57850         }
57851         for(var i = 0, len = rows.length; i < len; i++){
57852             this.selectRow(rows[i], true);
57853         }
57854     },
57855
57856     /**
57857      * Selects a range of rows. All rows in between startRow and endRow are also selected.
57858      * @param {Number} startRow The index of the first row in the range
57859      * @param {Number} endRow The index of the last row in the range
57860      * @param {Boolean} keepExisting (optional) True to retain existing selections
57861      */
57862     selectRange : function(startRow, endRow, keepExisting){
57863         if(this.locked) {
57864             return;
57865         }
57866         if(!keepExisting){
57867             this.clearSelections();
57868         }
57869         if(startRow <= endRow){
57870             for(var i = startRow; i <= endRow; i++){
57871                 this.selectRow(i, true);
57872             }
57873         }else{
57874             for(var i = startRow; i >= endRow; i--){
57875                 this.selectRow(i, true);
57876             }
57877         }
57878     },
57879
57880     /**
57881      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
57882      * @param {Number} startRow The index of the first row in the range
57883      * @param {Number} endRow The index of the last row in the range
57884      */
57885     deselectRange : function(startRow, endRow, preventViewNotify){
57886         if(this.locked) {
57887             return;
57888         }
57889         for(var i = startRow; i <= endRow; i++){
57890             this.deselectRow(i, preventViewNotify);
57891         }
57892     },
57893
57894     /**
57895      * Selects a row.
57896      * @param {Number} row The index of the row to select
57897      * @param {Boolean} keepExisting (optional) True to keep existing selections
57898      */
57899     selectRow : function(index, keepExisting, preventViewNotify){
57900         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
57901             return;
57902         }
57903         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
57904             if(!keepExisting || this.singleSelect){
57905                 this.clearSelections();
57906             }
57907             var r = this.grid.dataSource.getAt(index);
57908             this.selections.add(r);
57909             this.last = this.lastActive = index;
57910             if(!preventViewNotify){
57911                 this.grid.getView().onRowSelect(index);
57912             }
57913             this.fireEvent("rowselect", this, index, r);
57914             this.fireEvent("selectionchange", this);
57915         }
57916     },
57917
57918     /**
57919      * Deselects a row.
57920      * @param {Number} row The index of the row to deselect
57921      */
57922     deselectRow : function(index, preventViewNotify){
57923         if(this.locked) {
57924             return;
57925         }
57926         if(this.last == index){
57927             this.last = false;
57928         }
57929         if(this.lastActive == index){
57930             this.lastActive = false;
57931         }
57932         var r = this.grid.dataSource.getAt(index);
57933         this.selections.remove(r);
57934         if(!preventViewNotify){
57935             this.grid.getView().onRowDeselect(index);
57936         }
57937         this.fireEvent("rowdeselect", this, index);
57938         this.fireEvent("selectionchange", this);
57939     },
57940
57941     // private
57942     restoreLast : function(){
57943         if(this._last){
57944             this.last = this._last;
57945         }
57946     },
57947
57948     // private
57949     acceptsNav : function(row, col, cm){
57950         return !cm.isHidden(col) && cm.isCellEditable(col, row);
57951     },
57952
57953     // private
57954     onEditorKey : function(field, e){
57955         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
57956         if(k == e.TAB){
57957             e.stopEvent();
57958             ed.completeEdit();
57959             if(e.shiftKey){
57960                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
57961             }else{
57962                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
57963             }
57964         }else if(k == e.ENTER && !e.ctrlKey){
57965             e.stopEvent();
57966             ed.completeEdit();
57967             if(e.shiftKey){
57968                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
57969             }else{
57970                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
57971             }
57972         }else if(k == e.ESC){
57973             ed.cancelEdit();
57974         }
57975         if(newCell){
57976             g.startEditing(newCell[0], newCell[1]);
57977         }
57978     }
57979 });/*
57980  * Based on:
57981  * Ext JS Library 1.1.1
57982  * Copyright(c) 2006-2007, Ext JS, LLC.
57983  *
57984  * Originally Released Under LGPL - original licence link has changed is not relivant.
57985  *
57986  * Fork - LGPL
57987  * <script type="text/javascript">
57988  */
57989 /**
57990  * @class Roo.grid.CellSelectionModel
57991  * @extends Roo.grid.AbstractSelectionModel
57992  * This class provides the basic implementation for cell selection in a grid.
57993  * @constructor
57994  * @param {Object} config The object containing the configuration of this model.
57995  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
57996  */
57997 Roo.grid.CellSelectionModel = function(config){
57998     Roo.apply(this, config);
57999
58000     this.selection = null;
58001
58002     this.addEvents({
58003         /**
58004              * @event beforerowselect
58005              * Fires before a cell is selected.
58006              * @param {SelectionModel} this
58007              * @param {Number} rowIndex The selected row index
58008              * @param {Number} colIndex The selected cell index
58009              */
58010             "beforecellselect" : true,
58011         /**
58012              * @event cellselect
58013              * Fires when a cell is selected.
58014              * @param {SelectionModel} this
58015              * @param {Number} rowIndex The selected row index
58016              * @param {Number} colIndex The selected cell index
58017              */
58018             "cellselect" : true,
58019         /**
58020              * @event selectionchange
58021              * Fires when the active selection changes.
58022              * @param {SelectionModel} this
58023              * @param {Object} selection null for no selection or an object (o) with two properties
58024                 <ul>
58025                 <li>o.record: the record object for the row the selection is in</li>
58026                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58027                 </ul>
58028              */
58029             "selectionchange" : true,
58030         /**
58031              * @event tabend
58032              * Fires when the tab (or enter) was pressed on the last editable cell
58033              * You can use this to trigger add new row.
58034              * @param {SelectionModel} this
58035              */
58036             "tabend" : true,
58037          /**
58038              * @event beforeeditnext
58039              * Fires before the next editable sell is made active
58040              * You can use this to skip to another cell or fire the tabend
58041              *    if you set cell to false
58042              * @param {Object} eventdata object : { cell : [ row, col ] } 
58043              */
58044             "beforeeditnext" : true
58045     });
58046     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58047 };
58048
58049 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58050     
58051     enter_is_tab: false,
58052
58053     /** @ignore */
58054     initEvents : function(){
58055         this.grid.on("mousedown", this.handleMouseDown, this);
58056         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58057         var view = this.grid.view;
58058         view.on("refresh", this.onViewChange, this);
58059         view.on("rowupdated", this.onRowUpdated, this);
58060         view.on("beforerowremoved", this.clearSelections, this);
58061         view.on("beforerowsinserted", this.clearSelections, this);
58062         if(this.grid.isEditor){
58063             this.grid.on("beforeedit", this.beforeEdit,  this);
58064         }
58065     },
58066
58067         //private
58068     beforeEdit : function(e){
58069         this.select(e.row, e.column, false, true, e.record);
58070     },
58071
58072         //private
58073     onRowUpdated : function(v, index, r){
58074         if(this.selection && this.selection.record == r){
58075             v.onCellSelect(index, this.selection.cell[1]);
58076         }
58077     },
58078
58079         //private
58080     onViewChange : function(){
58081         this.clearSelections(true);
58082     },
58083
58084         /**
58085          * Returns the currently selected cell,.
58086          * @return {Array} The selected cell (row, column) or null if none selected.
58087          */
58088     getSelectedCell : function(){
58089         return this.selection ? this.selection.cell : null;
58090     },
58091
58092     /**
58093      * Clears all selections.
58094      * @param {Boolean} true to prevent the gridview from being notified about the change.
58095      */
58096     clearSelections : function(preventNotify){
58097         var s = this.selection;
58098         if(s){
58099             if(preventNotify !== true){
58100                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58101             }
58102             this.selection = null;
58103             this.fireEvent("selectionchange", this, null);
58104         }
58105     },
58106
58107     /**
58108      * Returns true if there is a selection.
58109      * @return {Boolean}
58110      */
58111     hasSelection : function(){
58112         return this.selection ? true : false;
58113     },
58114
58115     /** @ignore */
58116     handleMouseDown : function(e, t){
58117         var v = this.grid.getView();
58118         if(this.isLocked()){
58119             return;
58120         };
58121         var row = v.findRowIndex(t);
58122         var cell = v.findCellIndex(t);
58123         if(row !== false && cell !== false){
58124             this.select(row, cell);
58125         }
58126     },
58127
58128     /**
58129      * Selects a cell.
58130      * @param {Number} rowIndex
58131      * @param {Number} collIndex
58132      */
58133     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58134         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58135             this.clearSelections();
58136             r = r || this.grid.dataSource.getAt(rowIndex);
58137             this.selection = {
58138                 record : r,
58139                 cell : [rowIndex, colIndex]
58140             };
58141             if(!preventViewNotify){
58142                 var v = this.grid.getView();
58143                 v.onCellSelect(rowIndex, colIndex);
58144                 if(preventFocus !== true){
58145                     v.focusCell(rowIndex, colIndex);
58146                 }
58147             }
58148             this.fireEvent("cellselect", this, rowIndex, colIndex);
58149             this.fireEvent("selectionchange", this, this.selection);
58150         }
58151     },
58152
58153         //private
58154     isSelectable : function(rowIndex, colIndex, cm){
58155         return !cm.isHidden(colIndex);
58156     },
58157
58158     /** @ignore */
58159     handleKeyDown : function(e){
58160         //Roo.log('Cell Sel Model handleKeyDown');
58161         if(!e.isNavKeyPress()){
58162             return;
58163         }
58164         var g = this.grid, s = this.selection;
58165         if(!s){
58166             e.stopEvent();
58167             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
58168             if(cell){
58169                 this.select(cell[0], cell[1]);
58170             }
58171             return;
58172         }
58173         var sm = this;
58174         var walk = function(row, col, step){
58175             return g.walkCells(row, col, step, sm.isSelectable,  sm);
58176         };
58177         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
58178         var newCell;
58179
58180       
58181
58182         switch(k){
58183             case e.TAB:
58184                 // handled by onEditorKey
58185                 if (g.isEditor && g.editing) {
58186                     return;
58187                 }
58188                 if(e.shiftKey) {
58189                     newCell = walk(r, c-1, -1);
58190                 } else {
58191                     newCell = walk(r, c+1, 1);
58192                 }
58193                 break;
58194             
58195             case e.DOWN:
58196                newCell = walk(r+1, c, 1);
58197                 break;
58198             
58199             case e.UP:
58200                 newCell = walk(r-1, c, -1);
58201                 break;
58202             
58203             case e.RIGHT:
58204                 newCell = walk(r, c+1, 1);
58205                 break;
58206             
58207             case e.LEFT:
58208                 newCell = walk(r, c-1, -1);
58209                 break;
58210             
58211             case e.ENTER:
58212                 
58213                 if(g.isEditor && !g.editing){
58214                    g.startEditing(r, c);
58215                    e.stopEvent();
58216                    return;
58217                 }
58218                 
58219                 
58220              break;
58221         };
58222         if(newCell){
58223             this.select(newCell[0], newCell[1]);
58224             e.stopEvent();
58225             
58226         }
58227     },
58228
58229     acceptsNav : function(row, col, cm){
58230         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58231     },
58232     /**
58233      * Selects a cell.
58234      * @param {Number} field (not used) - as it's normally used as a listener
58235      * @param {Number} e - event - fake it by using
58236      *
58237      * var e = Roo.EventObjectImpl.prototype;
58238      * e.keyCode = e.TAB
58239      *
58240      * 
58241      */
58242     onEditorKey : function(field, e){
58243         
58244         var k = e.getKey(),
58245             newCell,
58246             g = this.grid,
58247             ed = g.activeEditor,
58248             forward = false;
58249         ///Roo.log('onEditorKey' + k);
58250         
58251         
58252         if (this.enter_is_tab && k == e.ENTER) {
58253             k = e.TAB;
58254         }
58255         
58256         if(k == e.TAB){
58257             if(e.shiftKey){
58258                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58259             }else{
58260                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58261                 forward = true;
58262             }
58263             
58264             e.stopEvent();
58265             
58266         } else if(k == e.ENTER &&  !e.ctrlKey){
58267             ed.completeEdit();
58268             e.stopEvent();
58269             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58270         
58271                 } else if(k == e.ESC){
58272             ed.cancelEdit();
58273         }
58274                 
58275         if (newCell) {
58276             var ecall = { cell : newCell, forward : forward };
58277             this.fireEvent('beforeeditnext', ecall );
58278             newCell = ecall.cell;
58279                         forward = ecall.forward;
58280         }
58281                 
58282         if(newCell){
58283             //Roo.log('next cell after edit');
58284             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
58285         } else if (forward) {
58286             // tabbed past last
58287             this.fireEvent.defer(100, this, ['tabend',this]);
58288         }
58289     }
58290 });/*
58291  * Based on:
58292  * Ext JS Library 1.1.1
58293  * Copyright(c) 2006-2007, Ext JS, LLC.
58294  *
58295  * Originally Released Under LGPL - original licence link has changed is not relivant.
58296  *
58297  * Fork - LGPL
58298  * <script type="text/javascript">
58299  */
58300  
58301 /**
58302  * @class Roo.grid.EditorGrid
58303  * @extends Roo.grid.Grid
58304  * Class for creating and editable grid.
58305  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
58306  * The container MUST have some type of size defined for the grid to fill. The container will be 
58307  * automatically set to position relative if it isn't already.
58308  * @param {Object} dataSource The data model to bind to
58309  * @param {Object} colModel The column model with info about this grid's columns
58310  */
58311 Roo.grid.EditorGrid = function(container, config){
58312     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
58313     this.getGridEl().addClass("xedit-grid");
58314
58315     if(!this.selModel){
58316         this.selModel = new Roo.grid.CellSelectionModel();
58317     }
58318
58319     this.activeEditor = null;
58320
58321         this.addEvents({
58322             /**
58323              * @event beforeedit
58324              * Fires before cell editing is triggered. The edit event object has the following properties <br />
58325              * <ul style="padding:5px;padding-left:16px;">
58326              * <li>grid - This grid</li>
58327              * <li>record - The record being edited</li>
58328              * <li>field - The field name being edited</li>
58329              * <li>value - The value for the field being edited.</li>
58330              * <li>row - The grid row index</li>
58331              * <li>column - The grid column index</li>
58332              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58333              * </ul>
58334              * @param {Object} e An edit event (see above for description)
58335              */
58336             "beforeedit" : true,
58337             /**
58338              * @event afteredit
58339              * Fires after a cell is edited. <br />
58340              * <ul style="padding:5px;padding-left:16px;">
58341              * <li>grid - This grid</li>
58342              * <li>record - The record being edited</li>
58343              * <li>field - The field name being edited</li>
58344              * <li>value - The value being set</li>
58345              * <li>originalValue - The original value for the field, before the edit.</li>
58346              * <li>row - The grid row index</li>
58347              * <li>column - The grid column index</li>
58348              * </ul>
58349              * @param {Object} e An edit event (see above for description)
58350              */
58351             "afteredit" : true,
58352             /**
58353              * @event validateedit
58354              * Fires after a cell is edited, but before the value is set in the record. 
58355          * You can use this to modify the value being set in the field, Return false
58356              * to cancel the change. The edit event object has the following properties <br />
58357              * <ul style="padding:5px;padding-left:16px;">
58358          * <li>editor - This editor</li>
58359              * <li>grid - This grid</li>
58360              * <li>record - The record being edited</li>
58361              * <li>field - The field name being edited</li>
58362              * <li>value - The value being set</li>
58363              * <li>originalValue - The original value for the field, before the edit.</li>
58364              * <li>row - The grid row index</li>
58365              * <li>column - The grid column index</li>
58366              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
58367              * </ul>
58368              * @param {Object} e An edit event (see above for description)
58369              */
58370             "validateedit" : true
58371         });
58372     this.on("bodyscroll", this.stopEditing,  this);
58373     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
58374 };
58375
58376 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
58377     /**
58378      * @cfg {Number} clicksToEdit
58379      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
58380      */
58381     clicksToEdit: 2,
58382
58383     // private
58384     isEditor : true,
58385     // private
58386     trackMouseOver: false, // causes very odd FF errors
58387
58388     onCellDblClick : function(g, row, col){
58389         this.startEditing(row, col);
58390     },
58391
58392     onEditComplete : function(ed, value, startValue){
58393         this.editing = false;
58394         this.activeEditor = null;
58395         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
58396         var r = ed.record;
58397         var field = this.colModel.getDataIndex(ed.col);
58398         var e = {
58399             grid: this,
58400             record: r,
58401             field: field,
58402             originalValue: startValue,
58403             value: value,
58404             row: ed.row,
58405             column: ed.col,
58406             cancel:false,
58407             editor: ed
58408         };
58409         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
58410         cell.show();
58411           
58412         if(String(value) !== String(startValue)){
58413             
58414             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
58415                 r.set(field, e.value);
58416                 // if we are dealing with a combo box..
58417                 // then we also set the 'name' colum to be the displayField
58418                 if (ed.field.displayField && ed.field.name) {
58419                     r.set(ed.field.name, ed.field.el.dom.value);
58420                 }
58421                 
58422                 delete e.cancel; //?? why!!!
58423                 this.fireEvent("afteredit", e);
58424             }
58425         } else {
58426             this.fireEvent("afteredit", e); // always fire it!
58427         }
58428         this.view.focusCell(ed.row, ed.col);
58429     },
58430
58431     /**
58432      * Starts editing the specified for the specified row/column
58433      * @param {Number} rowIndex
58434      * @param {Number} colIndex
58435      */
58436     startEditing : function(row, col){
58437         this.stopEditing();
58438         if(this.colModel.isCellEditable(col, row)){
58439             this.view.ensureVisible(row, col, true);
58440           
58441             var r = this.dataSource.getAt(row);
58442             var field = this.colModel.getDataIndex(col);
58443             var cell = Roo.get(this.view.getCell(row,col));
58444             var e = {
58445                 grid: this,
58446                 record: r,
58447                 field: field,
58448                 value: r.data[field],
58449                 row: row,
58450                 column: col,
58451                 cancel:false 
58452             };
58453             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
58454                 this.editing = true;
58455                 var ed = this.colModel.getCellEditor(col, row);
58456                 
58457                 if (!ed) {
58458                     return;
58459                 }
58460                 if(!ed.rendered){
58461                     ed.render(ed.parentEl || document.body);
58462                 }
58463                 ed.field.reset();
58464                
58465                 cell.hide();
58466                 
58467                 (function(){ // complex but required for focus issues in safari, ie and opera
58468                     ed.row = row;
58469                     ed.col = col;
58470                     ed.record = r;
58471                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
58472                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
58473                     this.activeEditor = ed;
58474                     var v = r.data[field];
58475                     ed.startEdit(this.view.getCell(row, col), v);
58476                     // combo's with 'displayField and name set
58477                     if (ed.field.displayField && ed.field.name) {
58478                         ed.field.el.dom.value = r.data[ed.field.name];
58479                     }
58480                     
58481                     
58482                 }).defer(50, this);
58483             }
58484         }
58485     },
58486         
58487     /**
58488      * Stops any active editing
58489      */
58490     stopEditing : function(){
58491         if(this.activeEditor){
58492             this.activeEditor.completeEdit();
58493         }
58494         this.activeEditor = null;
58495     },
58496         
58497          /**
58498      * Called to get grid's drag proxy text, by default returns this.ddText.
58499      * @return {String}
58500      */
58501     getDragDropText : function(){
58502         var count = this.selModel.getSelectedCell() ? 1 : 0;
58503         return String.format(this.ddText, count, count == 1 ? '' : 's');
58504     }
58505         
58506 });/*
58507  * Based on:
58508  * Ext JS Library 1.1.1
58509  * Copyright(c) 2006-2007, Ext JS, LLC.
58510  *
58511  * Originally Released Under LGPL - original licence link has changed is not relivant.
58512  *
58513  * Fork - LGPL
58514  * <script type="text/javascript">
58515  */
58516
58517 // private - not really -- you end up using it !
58518 // This is a support class used internally by the Grid components
58519
58520 /**
58521  * @class Roo.grid.GridEditor
58522  * @extends Roo.Editor
58523  * Class for creating and editable grid elements.
58524  * @param {Object} config any settings (must include field)
58525  */
58526 Roo.grid.GridEditor = function(field, config){
58527     if (!config && field.field) {
58528         config = field;
58529         field = Roo.factory(config.field, Roo.form);
58530     }
58531     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
58532     field.monitorTab = false;
58533 };
58534
58535 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
58536     
58537     /**
58538      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
58539      */
58540     
58541     alignment: "tl-tl",
58542     autoSize: "width",
58543     hideEl : false,
58544     cls: "x-small-editor x-grid-editor",
58545     shim:false,
58546     shadow:"frame"
58547 });/*
58548  * Based on:
58549  * Ext JS Library 1.1.1
58550  * Copyright(c) 2006-2007, Ext JS, LLC.
58551  *
58552  * Originally Released Under LGPL - original licence link has changed is not relivant.
58553  *
58554  * Fork - LGPL
58555  * <script type="text/javascript">
58556  */
58557   
58558
58559   
58560 Roo.grid.PropertyRecord = Roo.data.Record.create([
58561     {name:'name',type:'string'},  'value'
58562 ]);
58563
58564
58565 Roo.grid.PropertyStore = function(grid, source){
58566     this.grid = grid;
58567     this.store = new Roo.data.Store({
58568         recordType : Roo.grid.PropertyRecord
58569     });
58570     this.store.on('update', this.onUpdate,  this);
58571     if(source){
58572         this.setSource(source);
58573     }
58574     Roo.grid.PropertyStore.superclass.constructor.call(this);
58575 };
58576
58577
58578
58579 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
58580     setSource : function(o){
58581         this.source = o;
58582         this.store.removeAll();
58583         var data = [];
58584         for(var k in o){
58585             if(this.isEditableValue(o[k])){
58586                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
58587             }
58588         }
58589         this.store.loadRecords({records: data}, {}, true);
58590     },
58591
58592     onUpdate : function(ds, record, type){
58593         if(type == Roo.data.Record.EDIT){
58594             var v = record.data['value'];
58595             var oldValue = record.modified['value'];
58596             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
58597                 this.source[record.id] = v;
58598                 record.commit();
58599                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
58600             }else{
58601                 record.reject();
58602             }
58603         }
58604     },
58605
58606     getProperty : function(row){
58607        return this.store.getAt(row);
58608     },
58609
58610     isEditableValue: function(val){
58611         if(val && val instanceof Date){
58612             return true;
58613         }else if(typeof val == 'object' || typeof val == 'function'){
58614             return false;
58615         }
58616         return true;
58617     },
58618
58619     setValue : function(prop, value){
58620         this.source[prop] = value;
58621         this.store.getById(prop).set('value', value);
58622     },
58623
58624     getSource : function(){
58625         return this.source;
58626     }
58627 });
58628
58629 Roo.grid.PropertyColumnModel = function(grid, store){
58630     this.grid = grid;
58631     var g = Roo.grid;
58632     g.PropertyColumnModel.superclass.constructor.call(this, [
58633         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
58634         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
58635     ]);
58636     this.store = store;
58637     this.bselect = Roo.DomHelper.append(document.body, {
58638         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
58639             {tag: 'option', value: 'true', html: 'true'},
58640             {tag: 'option', value: 'false', html: 'false'}
58641         ]
58642     });
58643     Roo.id(this.bselect);
58644     var f = Roo.form;
58645     this.editors = {
58646         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
58647         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
58648         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
58649         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
58650         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
58651     };
58652     this.renderCellDelegate = this.renderCell.createDelegate(this);
58653     this.renderPropDelegate = this.renderProp.createDelegate(this);
58654 };
58655
58656 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
58657     
58658     
58659     nameText : 'Name',
58660     valueText : 'Value',
58661     
58662     dateFormat : 'm/j/Y',
58663     
58664     
58665     renderDate : function(dateVal){
58666         return dateVal.dateFormat(this.dateFormat);
58667     },
58668
58669     renderBool : function(bVal){
58670         return bVal ? 'true' : 'false';
58671     },
58672
58673     isCellEditable : function(colIndex, rowIndex){
58674         return colIndex == 1;
58675     },
58676
58677     getRenderer : function(col){
58678         return col == 1 ?
58679             this.renderCellDelegate : this.renderPropDelegate;
58680     },
58681
58682     renderProp : function(v){
58683         return this.getPropertyName(v);
58684     },
58685
58686     renderCell : function(val){
58687         var rv = val;
58688         if(val instanceof Date){
58689             rv = this.renderDate(val);
58690         }else if(typeof val == 'boolean'){
58691             rv = this.renderBool(val);
58692         }
58693         return Roo.util.Format.htmlEncode(rv);
58694     },
58695
58696     getPropertyName : function(name){
58697         var pn = this.grid.propertyNames;
58698         return pn && pn[name] ? pn[name] : name;
58699     },
58700
58701     getCellEditor : function(colIndex, rowIndex){
58702         var p = this.store.getProperty(rowIndex);
58703         var n = p.data['name'], val = p.data['value'];
58704         
58705         if(typeof(this.grid.customEditors[n]) == 'string'){
58706             return this.editors[this.grid.customEditors[n]];
58707         }
58708         if(typeof(this.grid.customEditors[n]) != 'undefined'){
58709             return this.grid.customEditors[n];
58710         }
58711         if(val instanceof Date){
58712             return this.editors['date'];
58713         }else if(typeof val == 'number'){
58714             return this.editors['number'];
58715         }else if(typeof val == 'boolean'){
58716             return this.editors['boolean'];
58717         }else{
58718             return this.editors['string'];
58719         }
58720     }
58721 });
58722
58723 /**
58724  * @class Roo.grid.PropertyGrid
58725  * @extends Roo.grid.EditorGrid
58726  * This class represents the  interface of a component based property grid control.
58727  * <br><br>Usage:<pre><code>
58728  var grid = new Roo.grid.PropertyGrid("my-container-id", {
58729       
58730  });
58731  // set any options
58732  grid.render();
58733  * </code></pre>
58734   
58735  * @constructor
58736  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58737  * The container MUST have some type of size defined for the grid to fill. The container will be
58738  * automatically set to position relative if it isn't already.
58739  * @param {Object} config A config object that sets properties on this grid.
58740  */
58741 Roo.grid.PropertyGrid = function(container, config){
58742     config = config || {};
58743     var store = new Roo.grid.PropertyStore(this);
58744     this.store = store;
58745     var cm = new Roo.grid.PropertyColumnModel(this, store);
58746     store.store.sort('name', 'ASC');
58747     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
58748         ds: store.store,
58749         cm: cm,
58750         enableColLock:false,
58751         enableColumnMove:false,
58752         stripeRows:false,
58753         trackMouseOver: false,
58754         clicksToEdit:1
58755     }, config));
58756     this.getGridEl().addClass('x-props-grid');
58757     this.lastEditRow = null;
58758     this.on('columnresize', this.onColumnResize, this);
58759     this.addEvents({
58760          /**
58761              * @event beforepropertychange
58762              * Fires before a property changes (return false to stop?)
58763              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58764              * @param {String} id Record Id
58765              * @param {String} newval New Value
58766          * @param {String} oldval Old Value
58767              */
58768         "beforepropertychange": true,
58769         /**
58770              * @event propertychange
58771              * Fires after a property changes
58772              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
58773              * @param {String} id Record Id
58774              * @param {String} newval New Value
58775          * @param {String} oldval Old Value
58776              */
58777         "propertychange": true
58778     });
58779     this.customEditors = this.customEditors || {};
58780 };
58781 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
58782     
58783      /**
58784      * @cfg {Object} customEditors map of colnames=> custom editors.
58785      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
58786      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
58787      * false disables editing of the field.
58788          */
58789     
58790       /**
58791      * @cfg {Object} propertyNames map of property Names to their displayed value
58792          */
58793     
58794     render : function(){
58795         Roo.grid.PropertyGrid.superclass.render.call(this);
58796         this.autoSize.defer(100, this);
58797     },
58798
58799     autoSize : function(){
58800         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
58801         if(this.view){
58802             this.view.fitColumns();
58803         }
58804     },
58805
58806     onColumnResize : function(){
58807         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
58808         this.autoSize();
58809     },
58810     /**
58811      * Sets the data for the Grid
58812      * accepts a Key => Value object of all the elements avaiable.
58813      * @param {Object} data  to appear in grid.
58814      */
58815     setSource : function(source){
58816         this.store.setSource(source);
58817         //this.autoSize();
58818     },
58819     /**
58820      * Gets all the data from the grid.
58821      * @return {Object} data  data stored in grid
58822      */
58823     getSource : function(){
58824         return this.store.getSource();
58825     }
58826 });/*
58827   
58828  * Licence LGPL
58829  
58830  */
58831  
58832 /**
58833  * @class Roo.grid.Calendar
58834  * @extends Roo.util.Grid
58835  * This class extends the Grid to provide a calendar widget
58836  * <br><br>Usage:<pre><code>
58837  var grid = new Roo.grid.Calendar("my-container-id", {
58838      ds: myDataStore,
58839      cm: myColModel,
58840      selModel: mySelectionModel,
58841      autoSizeColumns: true,
58842      monitorWindowResize: false,
58843      trackMouseOver: true
58844      eventstore : real data store..
58845  });
58846  // set any options
58847  grid.render();
58848   
58849   * @constructor
58850  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58851  * The container MUST have some type of size defined for the grid to fill. The container will be
58852  * automatically set to position relative if it isn't already.
58853  * @param {Object} config A config object that sets properties on this grid.
58854  */
58855 Roo.grid.Calendar = function(container, config){
58856         // initialize the container
58857         this.container = Roo.get(container);
58858         this.container.update("");
58859         this.container.setStyle("overflow", "hidden");
58860     this.container.addClass('x-grid-container');
58861
58862     this.id = this.container.id;
58863
58864     Roo.apply(this, config);
58865     // check and correct shorthanded configs
58866     
58867     var rows = [];
58868     var d =1;
58869     for (var r = 0;r < 6;r++) {
58870         
58871         rows[r]=[];
58872         for (var c =0;c < 7;c++) {
58873             rows[r][c]= '';
58874         }
58875     }
58876     if (this.eventStore) {
58877         this.eventStore= Roo.factory(this.eventStore, Roo.data);
58878         this.eventStore.on('load',this.onLoad, this);
58879         this.eventStore.on('beforeload',this.clearEvents, this);
58880          
58881     }
58882     
58883     this.dataSource = new Roo.data.Store({
58884             proxy: new Roo.data.MemoryProxy(rows),
58885             reader: new Roo.data.ArrayReader({}, [
58886                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
58887     });
58888
58889     this.dataSource.load();
58890     this.ds = this.dataSource;
58891     this.ds.xmodule = this.xmodule || false;
58892     
58893     
58894     var cellRender = function(v,x,r)
58895     {
58896         return String.format(
58897             '<div class="fc-day  fc-widget-content"><div>' +
58898                 '<div class="fc-event-container"></div>' +
58899                 '<div class="fc-day-number">{0}</div>'+
58900                 
58901                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
58902             '</div></div>', v);
58903     
58904     }
58905     
58906     
58907     this.colModel = new Roo.grid.ColumnModel( [
58908         {
58909             xtype: 'ColumnModel',
58910             xns: Roo.grid,
58911             dataIndex : 'weekday0',
58912             header : 'Sunday',
58913             renderer : cellRender
58914         },
58915         {
58916             xtype: 'ColumnModel',
58917             xns: Roo.grid,
58918             dataIndex : 'weekday1',
58919             header : 'Monday',
58920             renderer : cellRender
58921         },
58922         {
58923             xtype: 'ColumnModel',
58924             xns: Roo.grid,
58925             dataIndex : 'weekday2',
58926             header : 'Tuesday',
58927             renderer : cellRender
58928         },
58929         {
58930             xtype: 'ColumnModel',
58931             xns: Roo.grid,
58932             dataIndex : 'weekday3',
58933             header : 'Wednesday',
58934             renderer : cellRender
58935         },
58936         {
58937             xtype: 'ColumnModel',
58938             xns: Roo.grid,
58939             dataIndex : 'weekday4',
58940             header : 'Thursday',
58941             renderer : cellRender
58942         },
58943         {
58944             xtype: 'ColumnModel',
58945             xns: Roo.grid,
58946             dataIndex : 'weekday5',
58947             header : 'Friday',
58948             renderer : cellRender
58949         },
58950         {
58951             xtype: 'ColumnModel',
58952             xns: Roo.grid,
58953             dataIndex : 'weekday6',
58954             header : 'Saturday',
58955             renderer : cellRender
58956         }
58957     ]);
58958     this.cm = this.colModel;
58959     this.cm.xmodule = this.xmodule || false;
58960  
58961         
58962           
58963     //this.selModel = new Roo.grid.CellSelectionModel();
58964     //this.sm = this.selModel;
58965     //this.selModel.init(this);
58966     
58967     
58968     if(this.width){
58969         this.container.setWidth(this.width);
58970     }
58971
58972     if(this.height){
58973         this.container.setHeight(this.height);
58974     }
58975     /** @private */
58976         this.addEvents({
58977         // raw events
58978         /**
58979          * @event click
58980          * The raw click event for the entire grid.
58981          * @param {Roo.EventObject} e
58982          */
58983         "click" : true,
58984         /**
58985          * @event dblclick
58986          * The raw dblclick event for the entire grid.
58987          * @param {Roo.EventObject} e
58988          */
58989         "dblclick" : true,
58990         /**
58991          * @event contextmenu
58992          * The raw contextmenu event for the entire grid.
58993          * @param {Roo.EventObject} e
58994          */
58995         "contextmenu" : true,
58996         /**
58997          * @event mousedown
58998          * The raw mousedown event for the entire grid.
58999          * @param {Roo.EventObject} e
59000          */
59001         "mousedown" : true,
59002         /**
59003          * @event mouseup
59004          * The raw mouseup event for the entire grid.
59005          * @param {Roo.EventObject} e
59006          */
59007         "mouseup" : true,
59008         /**
59009          * @event mouseover
59010          * The raw mouseover event for the entire grid.
59011          * @param {Roo.EventObject} e
59012          */
59013         "mouseover" : true,
59014         /**
59015          * @event mouseout
59016          * The raw mouseout event for the entire grid.
59017          * @param {Roo.EventObject} e
59018          */
59019         "mouseout" : true,
59020         /**
59021          * @event keypress
59022          * The raw keypress event for the entire grid.
59023          * @param {Roo.EventObject} e
59024          */
59025         "keypress" : true,
59026         /**
59027          * @event keydown
59028          * The raw keydown event for the entire grid.
59029          * @param {Roo.EventObject} e
59030          */
59031         "keydown" : true,
59032
59033         // custom events
59034
59035         /**
59036          * @event cellclick
59037          * Fires when a cell is clicked
59038          * @param {Grid} this
59039          * @param {Number} rowIndex
59040          * @param {Number} columnIndex
59041          * @param {Roo.EventObject} e
59042          */
59043         "cellclick" : true,
59044         /**
59045          * @event celldblclick
59046          * Fires when a cell is double clicked
59047          * @param {Grid} this
59048          * @param {Number} rowIndex
59049          * @param {Number} columnIndex
59050          * @param {Roo.EventObject} e
59051          */
59052         "celldblclick" : true,
59053         /**
59054          * @event rowclick
59055          * Fires when a row is clicked
59056          * @param {Grid} this
59057          * @param {Number} rowIndex
59058          * @param {Roo.EventObject} e
59059          */
59060         "rowclick" : true,
59061         /**
59062          * @event rowdblclick
59063          * Fires when a row is double clicked
59064          * @param {Grid} this
59065          * @param {Number} rowIndex
59066          * @param {Roo.EventObject} e
59067          */
59068         "rowdblclick" : true,
59069         /**
59070          * @event headerclick
59071          * Fires when a header is clicked
59072          * @param {Grid} this
59073          * @param {Number} columnIndex
59074          * @param {Roo.EventObject} e
59075          */
59076         "headerclick" : true,
59077         /**
59078          * @event headerdblclick
59079          * Fires when a header cell is double clicked
59080          * @param {Grid} this
59081          * @param {Number} columnIndex
59082          * @param {Roo.EventObject} e
59083          */
59084         "headerdblclick" : true,
59085         /**
59086          * @event rowcontextmenu
59087          * Fires when a row is right clicked
59088          * @param {Grid} this
59089          * @param {Number} rowIndex
59090          * @param {Roo.EventObject} e
59091          */
59092         "rowcontextmenu" : true,
59093         /**
59094          * @event cellcontextmenu
59095          * Fires when a cell is right clicked
59096          * @param {Grid} this
59097          * @param {Number} rowIndex
59098          * @param {Number} cellIndex
59099          * @param {Roo.EventObject} e
59100          */
59101          "cellcontextmenu" : true,
59102         /**
59103          * @event headercontextmenu
59104          * Fires when a header is right clicked
59105          * @param {Grid} this
59106          * @param {Number} columnIndex
59107          * @param {Roo.EventObject} e
59108          */
59109         "headercontextmenu" : true,
59110         /**
59111          * @event bodyscroll
59112          * Fires when the body element is scrolled
59113          * @param {Number} scrollLeft
59114          * @param {Number} scrollTop
59115          */
59116         "bodyscroll" : true,
59117         /**
59118          * @event columnresize
59119          * Fires when the user resizes a column
59120          * @param {Number} columnIndex
59121          * @param {Number} newSize
59122          */
59123         "columnresize" : true,
59124         /**
59125          * @event columnmove
59126          * Fires when the user moves a column
59127          * @param {Number} oldIndex
59128          * @param {Number} newIndex
59129          */
59130         "columnmove" : true,
59131         /**
59132          * @event startdrag
59133          * Fires when row(s) start being dragged
59134          * @param {Grid} this
59135          * @param {Roo.GridDD} dd The drag drop object
59136          * @param {event} e The raw browser event
59137          */
59138         "startdrag" : true,
59139         /**
59140          * @event enddrag
59141          * Fires when a drag operation is complete
59142          * @param {Grid} this
59143          * @param {Roo.GridDD} dd The drag drop object
59144          * @param {event} e The raw browser event
59145          */
59146         "enddrag" : true,
59147         /**
59148          * @event dragdrop
59149          * Fires when dragged row(s) are dropped on a valid DD target
59150          * @param {Grid} this
59151          * @param {Roo.GridDD} dd The drag drop object
59152          * @param {String} targetId The target drag drop object
59153          * @param {event} e The raw browser event
59154          */
59155         "dragdrop" : true,
59156         /**
59157          * @event dragover
59158          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
59159          * @param {Grid} this
59160          * @param {Roo.GridDD} dd The drag drop object
59161          * @param {String} targetId The target drag drop object
59162          * @param {event} e The raw browser event
59163          */
59164         "dragover" : true,
59165         /**
59166          * @event dragenter
59167          *  Fires when the dragged row(s) first cross another DD target while being dragged
59168          * @param {Grid} this
59169          * @param {Roo.GridDD} dd The drag drop object
59170          * @param {String} targetId The target drag drop object
59171          * @param {event} e The raw browser event
59172          */
59173         "dragenter" : true,
59174         /**
59175          * @event dragout
59176          * Fires when the dragged row(s) leave another DD target while being dragged
59177          * @param {Grid} this
59178          * @param {Roo.GridDD} dd The drag drop object
59179          * @param {String} targetId The target drag drop object
59180          * @param {event} e The raw browser event
59181          */
59182         "dragout" : true,
59183         /**
59184          * @event rowclass
59185          * Fires when a row is rendered, so you can change add a style to it.
59186          * @param {GridView} gridview   The grid view
59187          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
59188          */
59189         'rowclass' : true,
59190
59191         /**
59192          * @event render
59193          * Fires when the grid is rendered
59194          * @param {Grid} grid
59195          */
59196         'render' : true,
59197             /**
59198              * @event select
59199              * Fires when a date is selected
59200              * @param {DatePicker} this
59201              * @param {Date} date The selected date
59202              */
59203         'select': true,
59204         /**
59205              * @event monthchange
59206              * Fires when the displayed month changes 
59207              * @param {DatePicker} this
59208              * @param {Date} date The selected month
59209              */
59210         'monthchange': true,
59211         /**
59212              * @event evententer
59213              * Fires when mouse over an event
59214              * @param {Calendar} this
59215              * @param {event} Event
59216              */
59217         'evententer': true,
59218         /**
59219              * @event eventleave
59220              * Fires when the mouse leaves an
59221              * @param {Calendar} this
59222              * @param {event}
59223              */
59224         'eventleave': true,
59225         /**
59226              * @event eventclick
59227              * Fires when the mouse click an
59228              * @param {Calendar} this
59229              * @param {event}
59230              */
59231         'eventclick': true,
59232         /**
59233              * @event eventrender
59234              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
59235              * @param {Calendar} this
59236              * @param {data} data to be modified
59237              */
59238         'eventrender': true
59239         
59240     });
59241
59242     Roo.grid.Grid.superclass.constructor.call(this);
59243     this.on('render', function() {
59244         this.view.el.addClass('x-grid-cal'); 
59245         
59246         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
59247
59248     },this);
59249     
59250     if (!Roo.grid.Calendar.style) {
59251         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
59252             
59253             
59254             '.x-grid-cal .x-grid-col' :  {
59255                 height: 'auto !important',
59256                 'vertical-align': 'top'
59257             },
59258             '.x-grid-cal  .fc-event-hori' : {
59259                 height: '14px'
59260             }
59261              
59262             
59263         }, Roo.id());
59264     }
59265
59266     
59267     
59268 };
59269 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
59270     /**
59271      * @cfg {Store} eventStore The store that loads events.
59272      */
59273     eventStore : 25,
59274
59275      
59276     activeDate : false,
59277     startDay : 0,
59278     autoWidth : true,
59279     monitorWindowResize : false,
59280
59281     
59282     resizeColumns : function() {
59283         var col = (this.view.el.getWidth() / 7) - 3;
59284         // loop through cols, and setWidth
59285         for(var i =0 ; i < 7 ; i++){
59286             this.cm.setColumnWidth(i, col);
59287         }
59288     },
59289      setDate :function(date) {
59290         
59291         Roo.log('setDate?');
59292         
59293         this.resizeColumns();
59294         var vd = this.activeDate;
59295         this.activeDate = date;
59296 //        if(vd && this.el){
59297 //            var t = date.getTime();
59298 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
59299 //                Roo.log('using add remove');
59300 //                
59301 //                this.fireEvent('monthchange', this, date);
59302 //                
59303 //                this.cells.removeClass("fc-state-highlight");
59304 //                this.cells.each(function(c){
59305 //                   if(c.dateValue == t){
59306 //                       c.addClass("fc-state-highlight");
59307 //                       setTimeout(function(){
59308 //                            try{c.dom.firstChild.focus();}catch(e){}
59309 //                       }, 50);
59310 //                       return false;
59311 //                   }
59312 //                   return true;
59313 //                });
59314 //                return;
59315 //            }
59316 //        }
59317         
59318         var days = date.getDaysInMonth();
59319         
59320         var firstOfMonth = date.getFirstDateOfMonth();
59321         var startingPos = firstOfMonth.getDay()-this.startDay;
59322         
59323         if(startingPos < this.startDay){
59324             startingPos += 7;
59325         }
59326         
59327         var pm = date.add(Date.MONTH, -1);
59328         var prevStart = pm.getDaysInMonth()-startingPos;
59329 //        
59330         
59331         
59332         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59333         
59334         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
59335         //this.cells.addClassOnOver('fc-state-hover');
59336         
59337         var cells = this.cells.elements;
59338         var textEls = this.textNodes;
59339         
59340         //Roo.each(cells, function(cell){
59341         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
59342         //});
59343         
59344         days += startingPos;
59345
59346         // convert everything to numbers so it's fast
59347         var day = 86400000;
59348         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
59349         //Roo.log(d);
59350         //Roo.log(pm);
59351         //Roo.log(prevStart);
59352         
59353         var today = new Date().clearTime().getTime();
59354         var sel = date.clearTime().getTime();
59355         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
59356         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
59357         var ddMatch = this.disabledDatesRE;
59358         var ddText = this.disabledDatesText;
59359         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
59360         var ddaysText = this.disabledDaysText;
59361         var format = this.format;
59362         
59363         var setCellClass = function(cal, cell){
59364             
59365             //Roo.log('set Cell Class');
59366             cell.title = "";
59367             var t = d.getTime();
59368             
59369             //Roo.log(d);
59370             
59371             
59372             cell.dateValue = t;
59373             if(t == today){
59374                 cell.className += " fc-today";
59375                 cell.className += " fc-state-highlight";
59376                 cell.title = cal.todayText;
59377             }
59378             if(t == sel){
59379                 // disable highlight in other month..
59380                 cell.className += " fc-state-highlight";
59381                 
59382             }
59383             // disabling
59384             if(t < min) {
59385                 //cell.className = " fc-state-disabled";
59386                 cell.title = cal.minText;
59387                 return;
59388             }
59389             if(t > max) {
59390                 //cell.className = " fc-state-disabled";
59391                 cell.title = cal.maxText;
59392                 return;
59393             }
59394             if(ddays){
59395                 if(ddays.indexOf(d.getDay()) != -1){
59396                     // cell.title = ddaysText;
59397                    // cell.className = " fc-state-disabled";
59398                 }
59399             }
59400             if(ddMatch && format){
59401                 var fvalue = d.dateFormat(format);
59402                 if(ddMatch.test(fvalue)){
59403                     cell.title = ddText.replace("%0", fvalue);
59404                    cell.className = " fc-state-disabled";
59405                 }
59406             }
59407             
59408             if (!cell.initialClassName) {
59409                 cell.initialClassName = cell.dom.className;
59410             }
59411             
59412             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
59413         };
59414
59415         var i = 0;
59416         
59417         for(; i < startingPos; i++) {
59418             cells[i].dayName =  (++prevStart);
59419             Roo.log(textEls[i]);
59420             d.setDate(d.getDate()+1);
59421             
59422             //cells[i].className = "fc-past fc-other-month";
59423             setCellClass(this, cells[i]);
59424         }
59425         
59426         var intDay = 0;
59427         
59428         for(; i < days; i++){
59429             intDay = i - startingPos + 1;
59430             cells[i].dayName =  (intDay);
59431             d.setDate(d.getDate()+1);
59432             
59433             cells[i].className = ''; // "x-date-active";
59434             setCellClass(this, cells[i]);
59435         }
59436         var extraDays = 0;
59437         
59438         for(; i < 42; i++) {
59439             //textEls[i].innerHTML = (++extraDays);
59440             
59441             d.setDate(d.getDate()+1);
59442             cells[i].dayName = (++extraDays);
59443             cells[i].className = "fc-future fc-other-month";
59444             setCellClass(this, cells[i]);
59445         }
59446         
59447         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
59448         
59449         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
59450         
59451         // this will cause all the cells to mis
59452         var rows= [];
59453         var i =0;
59454         for (var r = 0;r < 6;r++) {
59455             for (var c =0;c < 7;c++) {
59456                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
59457             }    
59458         }
59459         
59460         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
59461         for(i=0;i<cells.length;i++) {
59462             
59463             this.cells.elements[i].dayName = cells[i].dayName ;
59464             this.cells.elements[i].className = cells[i].className;
59465             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
59466             this.cells.elements[i].title = cells[i].title ;
59467             this.cells.elements[i].dateValue = cells[i].dateValue ;
59468         }
59469         
59470         
59471         
59472         
59473         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
59474         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
59475         
59476         ////if(totalRows != 6){
59477             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
59478            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
59479        // }
59480         
59481         this.fireEvent('monthchange', this, date);
59482         
59483         
59484     },
59485  /**
59486      * Returns the grid's SelectionModel.
59487      * @return {SelectionModel}
59488      */
59489     getSelectionModel : function(){
59490         if(!this.selModel){
59491             this.selModel = new Roo.grid.CellSelectionModel();
59492         }
59493         return this.selModel;
59494     },
59495
59496     load: function() {
59497         this.eventStore.load()
59498         
59499         
59500         
59501     },
59502     
59503     findCell : function(dt) {
59504         dt = dt.clearTime().getTime();
59505         var ret = false;
59506         this.cells.each(function(c){
59507             //Roo.log("check " +c.dateValue + '?=' + dt);
59508             if(c.dateValue == dt){
59509                 ret = c;
59510                 return false;
59511             }
59512             return true;
59513         });
59514         
59515         return ret;
59516     },
59517     
59518     findCells : function(rec) {
59519         var s = rec.data.start_dt.clone().clearTime().getTime();
59520        // Roo.log(s);
59521         var e= rec.data.end_dt.clone().clearTime().getTime();
59522        // Roo.log(e);
59523         var ret = [];
59524         this.cells.each(function(c){
59525              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
59526             
59527             if(c.dateValue > e){
59528                 return ;
59529             }
59530             if(c.dateValue < s){
59531                 return ;
59532             }
59533             ret.push(c);
59534         });
59535         
59536         return ret;    
59537     },
59538     
59539     findBestRow: function(cells)
59540     {
59541         var ret = 0;
59542         
59543         for (var i =0 ; i < cells.length;i++) {
59544             ret  = Math.max(cells[i].rows || 0,ret);
59545         }
59546         return ret;
59547         
59548     },
59549     
59550     
59551     addItem : function(rec)
59552     {
59553         // look for vertical location slot in
59554         var cells = this.findCells(rec);
59555         
59556         rec.row = this.findBestRow(cells);
59557         
59558         // work out the location.
59559         
59560         var crow = false;
59561         var rows = [];
59562         for(var i =0; i < cells.length; i++) {
59563             if (!crow) {
59564                 crow = {
59565                     start : cells[i],
59566                     end :  cells[i]
59567                 };
59568                 continue;
59569             }
59570             if (crow.start.getY() == cells[i].getY()) {
59571                 // on same row.
59572                 crow.end = cells[i];
59573                 continue;
59574             }
59575             // different row.
59576             rows.push(crow);
59577             crow = {
59578                 start: cells[i],
59579                 end : cells[i]
59580             };
59581             
59582         }
59583         
59584         rows.push(crow);
59585         rec.els = [];
59586         rec.rows = rows;
59587         rec.cells = cells;
59588         for (var i = 0; i < cells.length;i++) {
59589             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
59590             
59591         }
59592         
59593         
59594     },
59595     
59596     clearEvents: function() {
59597         
59598         if (!this.eventStore.getCount()) {
59599             return;
59600         }
59601         // reset number of rows in cells.
59602         Roo.each(this.cells.elements, function(c){
59603             c.rows = 0;
59604         });
59605         
59606         this.eventStore.each(function(e) {
59607             this.clearEvent(e);
59608         },this);
59609         
59610     },
59611     
59612     clearEvent : function(ev)
59613     {
59614         if (ev.els) {
59615             Roo.each(ev.els, function(el) {
59616                 el.un('mouseenter' ,this.onEventEnter, this);
59617                 el.un('mouseleave' ,this.onEventLeave, this);
59618                 el.remove();
59619             },this);
59620             ev.els = [];
59621         }
59622     },
59623     
59624     
59625     renderEvent : function(ev,ctr) {
59626         if (!ctr) {
59627              ctr = this.view.el.select('.fc-event-container',true).first();
59628         }
59629         
59630          
59631         this.clearEvent(ev);
59632             //code
59633        
59634         
59635         
59636         ev.els = [];
59637         var cells = ev.cells;
59638         var rows = ev.rows;
59639         this.fireEvent('eventrender', this, ev);
59640         
59641         for(var i =0; i < rows.length; i++) {
59642             
59643             cls = '';
59644             if (i == 0) {
59645                 cls += ' fc-event-start';
59646             }
59647             if ((i+1) == rows.length) {
59648                 cls += ' fc-event-end';
59649             }
59650             
59651             //Roo.log(ev.data);
59652             // how many rows should it span..
59653             var cg = this.eventTmpl.append(ctr,Roo.apply({
59654                 fccls : cls
59655                 
59656             }, ev.data) , true);
59657             
59658             
59659             cg.on('mouseenter' ,this.onEventEnter, this, ev);
59660             cg.on('mouseleave' ,this.onEventLeave, this, ev);
59661             cg.on('click', this.onEventClick, this, ev);
59662             
59663             ev.els.push(cg);
59664             
59665             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
59666             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
59667             //Roo.log(cg);
59668              
59669             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
59670             cg.setWidth(ebox.right - sbox.x -2);
59671         }
59672     },
59673     
59674     renderEvents: function()
59675     {   
59676         // first make sure there is enough space..
59677         
59678         if (!this.eventTmpl) {
59679             this.eventTmpl = new Roo.Template(
59680                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
59681                     '<div class="fc-event-inner">' +
59682                         '<span class="fc-event-time">{time}</span>' +
59683                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
59684                     '</div>' +
59685                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
59686                 '</div>'
59687             );
59688                 
59689         }
59690                
59691         
59692         
59693         this.cells.each(function(c) {
59694             //Roo.log(c.select('.fc-day-content div',true).first());
59695             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
59696         });
59697         
59698         var ctr = this.view.el.select('.fc-event-container',true).first();
59699         
59700         var cls;
59701         this.eventStore.each(function(ev){
59702             
59703             this.renderEvent(ev);
59704              
59705              
59706         }, this);
59707         this.view.layout();
59708         
59709     },
59710     
59711     onEventEnter: function (e, el,event,d) {
59712         this.fireEvent('evententer', this, el, event);
59713     },
59714     
59715     onEventLeave: function (e, el,event,d) {
59716         this.fireEvent('eventleave', this, el, event);
59717     },
59718     
59719     onEventClick: function (e, el,event,d) {
59720         this.fireEvent('eventclick', this, el, event);
59721     },
59722     
59723     onMonthChange: function () {
59724         this.store.load();
59725     },
59726     
59727     onLoad: function () {
59728         
59729         //Roo.log('calendar onload');
59730 //         
59731         if(this.eventStore.getCount() > 0){
59732             
59733            
59734             
59735             this.eventStore.each(function(d){
59736                 
59737                 
59738                 // FIXME..
59739                 var add =   d.data;
59740                 if (typeof(add.end_dt) == 'undefined')  {
59741                     Roo.log("Missing End time in calendar data: ");
59742                     Roo.log(d);
59743                     return;
59744                 }
59745                 if (typeof(add.start_dt) == 'undefined')  {
59746                     Roo.log("Missing Start time in calendar data: ");
59747                     Roo.log(d);
59748                     return;
59749                 }
59750                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
59751                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
59752                 add.id = add.id || d.id;
59753                 add.title = add.title || '??';
59754                 
59755                 this.addItem(d);
59756                 
59757              
59758             },this);
59759         }
59760         
59761         this.renderEvents();
59762     }
59763     
59764
59765 });
59766 /*
59767  grid : {
59768                 xtype: 'Grid',
59769                 xns: Roo.grid,
59770                 listeners : {
59771                     render : function ()
59772                     {
59773                         _this.grid = this;
59774                         
59775                         if (!this.view.el.hasClass('course-timesheet')) {
59776                             this.view.el.addClass('course-timesheet');
59777                         }
59778                         if (this.tsStyle) {
59779                             this.ds.load({});
59780                             return; 
59781                         }
59782                         Roo.log('width');
59783                         Roo.log(_this.grid.view.el.getWidth());
59784                         
59785                         
59786                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
59787                             '.course-timesheet .x-grid-row' : {
59788                                 height: '80px'
59789                             },
59790                             '.x-grid-row td' : {
59791                                 'vertical-align' : 0
59792                             },
59793                             '.course-edit-link' : {
59794                                 'color' : 'blue',
59795                                 'text-overflow' : 'ellipsis',
59796                                 'overflow' : 'hidden',
59797                                 'white-space' : 'nowrap',
59798                                 'cursor' : 'pointer'
59799                             },
59800                             '.sub-link' : {
59801                                 'color' : 'green'
59802                             },
59803                             '.de-act-sup-link' : {
59804                                 'color' : 'purple',
59805                                 'text-decoration' : 'line-through'
59806                             },
59807                             '.de-act-link' : {
59808                                 'color' : 'red',
59809                                 'text-decoration' : 'line-through'
59810                             },
59811                             '.course-timesheet .course-highlight' : {
59812                                 'border-top-style': 'dashed !important',
59813                                 'border-bottom-bottom': 'dashed !important'
59814                             },
59815                             '.course-timesheet .course-item' : {
59816                                 'font-family'   : 'tahoma, arial, helvetica',
59817                                 'font-size'     : '11px',
59818                                 'overflow'      : 'hidden',
59819                                 'padding-left'  : '10px',
59820                                 'padding-right' : '10px',
59821                                 'padding-top' : '10px' 
59822                             }
59823                             
59824                         }, Roo.id());
59825                                 this.ds.load({});
59826                     }
59827                 },
59828                 autoWidth : true,
59829                 monitorWindowResize : false,
59830                 cellrenderer : function(v,x,r)
59831                 {
59832                     return v;
59833                 },
59834                 sm : {
59835                     xtype: 'CellSelectionModel',
59836                     xns: Roo.grid
59837                 },
59838                 dataSource : {
59839                     xtype: 'Store',
59840                     xns: Roo.data,
59841                     listeners : {
59842                         beforeload : function (_self, options)
59843                         {
59844                             options.params = options.params || {};
59845                             options.params._month = _this.monthField.getValue();
59846                             options.params.limit = 9999;
59847                             options.params['sort'] = 'when_dt';    
59848                             options.params['dir'] = 'ASC';    
59849                             this.proxy.loadResponse = this.loadResponse;
59850                             Roo.log("load?");
59851                             //this.addColumns();
59852                         },
59853                         load : function (_self, records, options)
59854                         {
59855                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
59856                                 // if you click on the translation.. you can edit it...
59857                                 var el = Roo.get(this);
59858                                 var id = el.dom.getAttribute('data-id');
59859                                 var d = el.dom.getAttribute('data-date');
59860                                 var t = el.dom.getAttribute('data-time');
59861                                 //var id = this.child('span').dom.textContent;
59862                                 
59863                                 //Roo.log(this);
59864                                 Pman.Dialog.CourseCalendar.show({
59865                                     id : id,
59866                                     when_d : d,
59867                                     when_t : t,
59868                                     productitem_active : id ? 1 : 0
59869                                 }, function() {
59870                                     _this.grid.ds.load({});
59871                                 });
59872                            
59873                            });
59874                            
59875                            _this.panel.fireEvent('resize', [ '', '' ]);
59876                         }
59877                     },
59878                     loadResponse : function(o, success, response){
59879                             // this is overridden on before load..
59880                             
59881                             Roo.log("our code?");       
59882                             //Roo.log(success);
59883                             //Roo.log(response)
59884                             delete this.activeRequest;
59885                             if(!success){
59886                                 this.fireEvent("loadexception", this, o, response);
59887                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59888                                 return;
59889                             }
59890                             var result;
59891                             try {
59892                                 result = o.reader.read(response);
59893                             }catch(e){
59894                                 Roo.log("load exception?");
59895                                 this.fireEvent("loadexception", this, o, response, e);
59896                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
59897                                 return;
59898                             }
59899                             Roo.log("ready...");        
59900                             // loop through result.records;
59901                             // and set this.tdate[date] = [] << array of records..
59902                             _this.tdata  = {};
59903                             Roo.each(result.records, function(r){
59904                                 //Roo.log(r.data);
59905                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
59906                                     _this.tdata[r.data.when_dt.format('j')] = [];
59907                                 }
59908                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
59909                             });
59910                             
59911                             //Roo.log(_this.tdata);
59912                             
59913                             result.records = [];
59914                             result.totalRecords = 6;
59915                     
59916                             // let's generate some duumy records for the rows.
59917                             //var st = _this.dateField.getValue();
59918                             
59919                             // work out monday..
59920                             //st = st.add(Date.DAY, -1 * st.format('w'));
59921                             
59922                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
59923                             
59924                             var firstOfMonth = date.getFirstDayOfMonth();
59925                             var days = date.getDaysInMonth();
59926                             var d = 1;
59927                             var firstAdded = false;
59928                             for (var i = 0; i < result.totalRecords ; i++) {
59929                                 //var d= st.add(Date.DAY, i);
59930                                 var row = {};
59931                                 var added = 0;
59932                                 for(var w = 0 ; w < 7 ; w++){
59933                                     if(!firstAdded && firstOfMonth != w){
59934                                         continue;
59935                                     }
59936                                     if(d > days){
59937                                         continue;
59938                                     }
59939                                     firstAdded = true;
59940                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
59941                                     row['weekday'+w] = String.format(
59942                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
59943                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
59944                                                     d,
59945                                                     date.format('Y-m-')+dd
59946                                                 );
59947                                     added++;
59948                                     if(typeof(_this.tdata[d]) != 'undefined'){
59949                                         Roo.each(_this.tdata[d], function(r){
59950                                             var is_sub = '';
59951                                             var deactive = '';
59952                                             var id = r.id;
59953                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
59954                                             if(r.parent_id*1>0){
59955                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
59956                                                 id = r.parent_id;
59957                                             }
59958                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
59959                                                 deactive = 'de-act-link';
59960                                             }
59961                                             
59962                                             row['weekday'+w] += String.format(
59963                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
59964                                                     id, //0
59965                                                     r.product_id_name, //1
59966                                                     r.when_dt.format('h:ia'), //2
59967                                                     is_sub, //3
59968                                                     deactive, //4
59969                                                     desc // 5
59970                                             );
59971                                         });
59972                                     }
59973                                     d++;
59974                                 }
59975                                 
59976                                 // only do this if something added..
59977                                 if(added > 0){ 
59978                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
59979                                 }
59980                                 
59981                                 
59982                                 // push it twice. (second one with an hour..
59983                                 
59984                             }
59985                             //Roo.log(result);
59986                             this.fireEvent("load", this, o, o.request.arg);
59987                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
59988                         },
59989                     sortInfo : {field: 'when_dt', direction : 'ASC' },
59990                     proxy : {
59991                         xtype: 'HttpProxy',
59992                         xns: Roo.data,
59993                         method : 'GET',
59994                         url : baseURL + '/Roo/Shop_course.php'
59995                     },
59996                     reader : {
59997                         xtype: 'JsonReader',
59998                         xns: Roo.data,
59999                         id : 'id',
60000                         fields : [
60001                             {
60002                                 'name': 'id',
60003                                 'type': 'int'
60004                             },
60005                             {
60006                                 'name': 'when_dt',
60007                                 'type': 'string'
60008                             },
60009                             {
60010                                 'name': 'end_dt',
60011                                 'type': 'string'
60012                             },
60013                             {
60014                                 'name': 'parent_id',
60015                                 'type': 'int'
60016                             },
60017                             {
60018                                 'name': 'product_id',
60019                                 'type': 'int'
60020                             },
60021                             {
60022                                 'name': 'productitem_id',
60023                                 'type': 'int'
60024                             },
60025                             {
60026                                 'name': 'guid',
60027                                 'type': 'int'
60028                             }
60029                         ]
60030                     }
60031                 },
60032                 toolbar : {
60033                     xtype: 'Toolbar',
60034                     xns: Roo,
60035                     items : [
60036                         {
60037                             xtype: 'Button',
60038                             xns: Roo.Toolbar,
60039                             listeners : {
60040                                 click : function (_self, e)
60041                                 {
60042                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60043                                     sd.setMonth(sd.getMonth()-1);
60044                                     _this.monthField.setValue(sd.format('Y-m-d'));
60045                                     _this.grid.ds.load({});
60046                                 }
60047                             },
60048                             text : "Back"
60049                         },
60050                         {
60051                             xtype: 'Separator',
60052                             xns: Roo.Toolbar
60053                         },
60054                         {
60055                             xtype: 'MonthField',
60056                             xns: Roo.form,
60057                             listeners : {
60058                                 render : function (_self)
60059                                 {
60060                                     _this.monthField = _self;
60061                                    // _this.monthField.set  today
60062                                 },
60063                                 select : function (combo, date)
60064                                 {
60065                                     _this.grid.ds.load({});
60066                                 }
60067                             },
60068                             value : (function() { return new Date(); })()
60069                         },
60070                         {
60071                             xtype: 'Separator',
60072                             xns: Roo.Toolbar
60073                         },
60074                         {
60075                             xtype: 'TextItem',
60076                             xns: Roo.Toolbar,
60077                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60078                         },
60079                         {
60080                             xtype: 'Fill',
60081                             xns: Roo.Toolbar
60082                         },
60083                         {
60084                             xtype: 'Button',
60085                             xns: Roo.Toolbar,
60086                             listeners : {
60087                                 click : function (_self, e)
60088                                 {
60089                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60090                                     sd.setMonth(sd.getMonth()+1);
60091                                     _this.monthField.setValue(sd.format('Y-m-d'));
60092                                     _this.grid.ds.load({});
60093                                 }
60094                             },
60095                             text : "Next"
60096                         }
60097                     ]
60098                 },
60099                  
60100             }
60101         };
60102         
60103         *//*
60104  * Based on:
60105  * Ext JS Library 1.1.1
60106  * Copyright(c) 2006-2007, Ext JS, LLC.
60107  *
60108  * Originally Released Under LGPL - original licence link has changed is not relivant.
60109  *
60110  * Fork - LGPL
60111  * <script type="text/javascript">
60112  */
60113  
60114 /**
60115  * @class Roo.LoadMask
60116  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60117  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60118  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60119  * element's UpdateManager load indicator and will be destroyed after the initial load.
60120  * @constructor
60121  * Create a new LoadMask
60122  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60123  * @param {Object} config The config object
60124  */
60125 Roo.LoadMask = function(el, config){
60126     this.el = Roo.get(el);
60127     Roo.apply(this, config);
60128     if(this.store){
60129         this.store.on('beforeload', this.onBeforeLoad, this);
60130         this.store.on('load', this.onLoad, this);
60131         this.store.on('loadexception', this.onLoadException, this);
60132         this.removeMask = false;
60133     }else{
60134         var um = this.el.getUpdateManager();
60135         um.showLoadIndicator = false; // disable the default indicator
60136         um.on('beforeupdate', this.onBeforeLoad, this);
60137         um.on('update', this.onLoad, this);
60138         um.on('failure', this.onLoad, this);
60139         this.removeMask = true;
60140     }
60141 };
60142
60143 Roo.LoadMask.prototype = {
60144     /**
60145      * @cfg {Boolean} removeMask
60146      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
60147      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
60148      */
60149     /**
60150      * @cfg {String} msg
60151      * The text to display in a centered loading message box (defaults to 'Loading...')
60152      */
60153     msg : 'Loading...',
60154     /**
60155      * @cfg {String} msgCls
60156      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
60157      */
60158     msgCls : 'x-mask-loading',
60159
60160     /**
60161      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
60162      * @type Boolean
60163      */
60164     disabled: false,
60165
60166     /**
60167      * Disables the mask to prevent it from being displayed
60168      */
60169     disable : function(){
60170        this.disabled = true;
60171     },
60172
60173     /**
60174      * Enables the mask so that it can be displayed
60175      */
60176     enable : function(){
60177         this.disabled = false;
60178     },
60179     
60180     onLoadException : function()
60181     {
60182         Roo.log(arguments);
60183         
60184         if (typeof(arguments[3]) != 'undefined') {
60185             Roo.MessageBox.alert("Error loading",arguments[3]);
60186         } 
60187         /*
60188         try {
60189             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
60190                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
60191             }   
60192         } catch(e) {
60193             
60194         }
60195         */
60196     
60197         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60198     },
60199     // private
60200     onLoad : function()
60201     {
60202         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
60203     },
60204
60205     // private
60206     onBeforeLoad : function(){
60207         if(!this.disabled){
60208             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
60209         }
60210     },
60211
60212     // private
60213     destroy : function(){
60214         if(this.store){
60215             this.store.un('beforeload', this.onBeforeLoad, this);
60216             this.store.un('load', this.onLoad, this);
60217             this.store.un('loadexception', this.onLoadException, this);
60218         }else{
60219             var um = this.el.getUpdateManager();
60220             um.un('beforeupdate', this.onBeforeLoad, this);
60221             um.un('update', this.onLoad, this);
60222             um.un('failure', this.onLoad, this);
60223         }
60224     }
60225 };/*
60226  * Based on:
60227  * Ext JS Library 1.1.1
60228  * Copyright(c) 2006-2007, Ext JS, LLC.
60229  *
60230  * Originally Released Under LGPL - original licence link has changed is not relivant.
60231  *
60232  * Fork - LGPL
60233  * <script type="text/javascript">
60234  */
60235
60236
60237 /**
60238  * @class Roo.XTemplate
60239  * @extends Roo.Template
60240  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
60241 <pre><code>
60242 var t = new Roo.XTemplate(
60243         '&lt;select name="{name}"&gt;',
60244                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
60245         '&lt;/select&gt;'
60246 );
60247  
60248 // then append, applying the master template values
60249  </code></pre>
60250  *
60251  * Supported features:
60252  *
60253  *  Tags:
60254
60255 <pre><code>
60256       {a_variable} - output encoded.
60257       {a_variable.format:("Y-m-d")} - call a method on the variable
60258       {a_variable:raw} - unencoded output
60259       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
60260       {a_variable:this.method_on_template(...)} - call a method on the template object.
60261  
60262 </code></pre>
60263  *  The tpl tag:
60264 <pre><code>
60265         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
60266         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
60267         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
60268         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
60269   
60270         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
60271         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
60272 </code></pre>
60273  *      
60274  */
60275 Roo.XTemplate = function()
60276 {
60277     Roo.XTemplate.superclass.constructor.apply(this, arguments);
60278     if (this.html) {
60279         this.compile();
60280     }
60281 };
60282
60283
60284 Roo.extend(Roo.XTemplate, Roo.Template, {
60285
60286     /**
60287      * The various sub templates
60288      */
60289     tpls : false,
60290     /**
60291      *
60292      * basic tag replacing syntax
60293      * WORD:WORD()
60294      *
60295      * // you can fake an object call by doing this
60296      *  x.t:(test,tesT) 
60297      * 
60298      */
60299     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
60300
60301     /**
60302      * compile the template
60303      *
60304      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
60305      *
60306      */
60307     compile: function()
60308     {
60309         var s = this.html;
60310      
60311         s = ['<tpl>', s, '</tpl>'].join('');
60312     
60313         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
60314             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
60315             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
60316             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
60317             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
60318             m,
60319             id     = 0,
60320             tpls   = [];
60321     
60322         while(true == !!(m = s.match(re))){
60323             var forMatch   = m[0].match(nameRe),
60324                 ifMatch   = m[0].match(ifRe),
60325                 execMatch   = m[0].match(execRe),
60326                 namedMatch   = m[0].match(namedRe),
60327                 
60328                 exp  = null, 
60329                 fn   = null,
60330                 exec = null,
60331                 name = forMatch && forMatch[1] ? forMatch[1] : '';
60332                 
60333             if (ifMatch) {
60334                 // if - puts fn into test..
60335                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
60336                 if(exp){
60337                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
60338                 }
60339             }
60340             
60341             if (execMatch) {
60342                 // exec - calls a function... returns empty if true is  returned.
60343                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
60344                 if(exp){
60345                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
60346                 }
60347             }
60348             
60349             
60350             if (name) {
60351                 // for = 
60352                 switch(name){
60353                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
60354                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
60355                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
60356                 }
60357             }
60358             var uid = namedMatch ? namedMatch[1] : id;
60359             
60360             
60361             tpls.push({
60362                 id:     namedMatch ? namedMatch[1] : id,
60363                 target: name,
60364                 exec:   exec,
60365                 test:   fn,
60366                 body:   m[1] || ''
60367             });
60368             if (namedMatch) {
60369                 s = s.replace(m[0], '');
60370             } else { 
60371                 s = s.replace(m[0], '{xtpl'+ id + '}');
60372             }
60373             ++id;
60374         }
60375         this.tpls = [];
60376         for(var i = tpls.length-1; i >= 0; --i){
60377             this.compileTpl(tpls[i]);
60378             this.tpls[tpls[i].id] = tpls[i];
60379         }
60380         this.master = tpls[tpls.length-1];
60381         return this;
60382     },
60383     /**
60384      * same as applyTemplate, except it's done to one of the subTemplates
60385      * when using named templates, you can do:
60386      *
60387      * var str = pl.applySubTemplate('your-name', values);
60388      *
60389      * 
60390      * @param {Number} id of the template
60391      * @param {Object} values to apply to template
60392      * @param {Object} parent (normaly the instance of this object)
60393      */
60394     applySubTemplate : function(id, values, parent)
60395     {
60396         
60397         
60398         var t = this.tpls[id];
60399         
60400         
60401         try { 
60402             if(t.test && !t.test.call(this, values, parent)){
60403                 return '';
60404             }
60405         } catch(e) {
60406             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
60407             Roo.log(e.toString());
60408             Roo.log(t.test);
60409             return ''
60410         }
60411         try { 
60412             
60413             if(t.exec && t.exec.call(this, values, parent)){
60414                 return '';
60415             }
60416         } catch(e) {
60417             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
60418             Roo.log(e.toString());
60419             Roo.log(t.exec);
60420             return ''
60421         }
60422         try {
60423             var vs = t.target ? t.target.call(this, values, parent) : values;
60424             parent = t.target ? values : parent;
60425             if(t.target && vs instanceof Array){
60426                 var buf = [];
60427                 for(var i = 0, len = vs.length; i < len; i++){
60428                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
60429                 }
60430                 return buf.join('');
60431             }
60432             return t.compiled.call(this, vs, parent);
60433         } catch (e) {
60434             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
60435             Roo.log(e.toString());
60436             Roo.log(t.compiled);
60437             return '';
60438         }
60439     },
60440
60441     compileTpl : function(tpl)
60442     {
60443         var fm = Roo.util.Format;
60444         var useF = this.disableFormats !== true;
60445         var sep = Roo.isGecko ? "+" : ",";
60446         var undef = function(str) {
60447             Roo.log("Property not found :"  + str);
60448             return '';
60449         };
60450         
60451         var fn = function(m, name, format, args)
60452         {
60453             //Roo.log(arguments);
60454             args = args ? args.replace(/\\'/g,"'") : args;
60455             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
60456             if (typeof(format) == 'undefined') {
60457                 format= 'htmlEncode';
60458             }
60459             if (format == 'raw' ) {
60460                 format = false;
60461             }
60462             
60463             if(name.substr(0, 4) == 'xtpl'){
60464                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
60465             }
60466             
60467             // build an array of options to determine if value is undefined..
60468             
60469             // basically get 'xxxx.yyyy' then do
60470             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
60471             //    (function () { Roo.log("Property not found"); return ''; })() :
60472             //    ......
60473             
60474             var udef_ar = [];
60475             var lookfor = '';
60476             Roo.each(name.split('.'), function(st) {
60477                 lookfor += (lookfor.length ? '.': '') + st;
60478                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
60479             });
60480             
60481             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
60482             
60483             
60484             if(format && useF){
60485                 
60486                 args = args ? ',' + args : "";
60487                  
60488                 if(format.substr(0, 5) != "this."){
60489                     format = "fm." + format + '(';
60490                 }else{
60491                     format = 'this.call("'+ format.substr(5) + '", ';
60492                     args = ", values";
60493                 }
60494                 
60495                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
60496             }
60497              
60498             if (args.length) {
60499                 // called with xxyx.yuu:(test,test)
60500                 // change to ()
60501                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
60502             }
60503             // raw.. - :raw modifier..
60504             return "'"+ sep + udef_st  + name + ")"+sep+"'";
60505             
60506         };
60507         var body;
60508         // branched to use + in gecko and [].join() in others
60509         if(Roo.isGecko){
60510             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
60511                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
60512                     "';};};";
60513         }else{
60514             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
60515             body.push(tpl.body.replace(/(\r\n|\n)/g,
60516                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
60517             body.push("'].join('');};};");
60518             body = body.join('');
60519         }
60520         
60521         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
60522        
60523         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
60524         eval(body);
60525         
60526         return this;
60527     },
60528
60529     applyTemplate : function(values){
60530         return this.master.compiled.call(this, values, {});
60531         //var s = this.subs;
60532     },
60533
60534     apply : function(){
60535         return this.applyTemplate.apply(this, arguments);
60536     }
60537
60538  });
60539
60540 Roo.XTemplate.from = function(el){
60541     el = Roo.getDom(el);
60542     return new Roo.XTemplate(el.value || el.innerHTML);
60543 };